From 1d4acbdd172c7124d290794556617ab193343bee Mon Sep 17 00:00:00 2001 From: Mes Date: Sun, 15 Dec 2024 22:24:52 +0800 Subject: [PATCH 1/2] Migrate to ACLINT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To implement timer interrupts and inter-processor interrupts (IPIs) on RISC-V, ACLINT and CLINT are the commonly used hardware components. The key difference between ACLINT and CLINT lies in ACLINT’s ability to support both Supervisor software interrupts (SSWI) and Machine software interrupts (MSWI). Additionally, ACLINT modularizes its hardware functionalities, such as timers and IPI controllers, making the design and implementation more flexible than CLINT. According to the Linux kernel documentation: https://www.kernel.org/doc/html/next/riscv/boot.html#kernel-entry, there are two methods for entering the Linux kernel on SMP systems: - RISCV_BOOT_SPINWAIT: Boots all harts simultaneously, mainly used for older firmwares without SBI HSM extension and M-mode RISC-V kernels. - Ordered booting: Utilizes the SBI HSM extension to boot only one hart during the initial boot phase. The Linux kernel introduced ordered booting (commit 'cfafe26') to simplify multi-stage SMP boot management. The commit explains that the previous method complicated the multi-stage boot process, requiring management of all harts at each stage. The SBI HSM extension simplifies this by booting only one hart initially, which can then bring up the remaining harts sequentially. To fully support the HSM extension, ACLINT is necessary. particularly for supervisor-level interrupt management. This commit transitions from CLINT to ACLINT, aligning with modern RISC-V specifications and providing support for 'mtimer', 'mswi', and 'sswi'. The existing CLINT implementation has been removed entirely as ACLINT covers its functionalities. Testing instructions: - Run the following command to test the implementation: 'make check SMP=n', where 'n' is the number of harts to simulate. - After booting the emulator: - Verify multi-core operation and HSM implementation with '/proc/cpuinfo'. - Check timer interrupts via '/proc/interrupts'. - Confirm ACLINT is correctly recognized using '/proc/device-tree'. Future work: 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. Also, SMP support remains incomplete. For example, the current semu implementation sequentially simulates multi-core execution, causing a slowdown as the number of cores increases. This leads to a time desynchronization issue across cores. To achieve multi-threaded system emulation, RFENCE extension implementation is required. However, it is currently incomplete. After completing ACLINT, the next step is to implement the RFENCE extension to fully support multi-threaded system emulation. --- Makefile | 2 +- aclint.c | 218 ++++++++++++++++++++++++++++++++++++++++ clint.c | 96 ------------------ device.h | 105 +++++++++++++++---- main.c | 60 ++++++++--- riscv.c | 2 +- scripts/gen-hart-dts.py | 49 +++++++-- 7 files changed, 391 insertions(+), 141 deletions(-) create mode 100644 aclint.c delete mode 100644 clint.c 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..6626c09a 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,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,6 +613,11 @@ 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; @@ -616,6 +643,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)) From 817141441e43641363cae96469b80b44931d4920 Mon Sep 17 00:00:00 2001 From: Mes Date: Sun, 15 Dec 2024 22:30:49 +0800 Subject: [PATCH 2/2] Place 'TODO' comments to indicate the next step --- main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 6626c09a..c6e81cb9 100644 --- a/main.c +++ b/main.c @@ -302,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: @@ -622,6 +627,9 @@ static int semu_start(int argc, char **argv) /* 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;