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..d1a8ddb6 --- /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); +} \ No newline at end of file diff --git a/device.h b/device.h index 489cae6c..0a6a5217 100644 --- a/device.h +++ b/device.h @@ -172,6 +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) */ +#if SEMU_HAS(ACLINT) +/* ACLINT MTIMER */ +typedef struct { + 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; + +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); +#else /* clint */ typedef struct { uint32_t msip[4096]; @@ -190,9 +243,9 @@ void clint_write(hart_t *vm, uint32_t addr, uint8_t width, uint32_t value); +#endif /* memory mapping */ - typedef struct { bool stopped; uint32_t *ram; @@ -205,5 +258,12 @@ typedef struct { #if SEMU_HAS(VIRTIOBLK) virtio_blk_state_t vblk; #endif +#if SEMU_HAS(ACLINT) + /* ACLINT */ + mtimer_state_t mtimer; + mswi_state_t mswi; + sswi_state_t sswi; +#else clint_state_t clint; +#endif } emu_state_t; diff --git a/feature.h b/feature.h index 1dee984a..810fb3c7 100644 --- a/feature.h +++ b/feature.h @@ -12,5 +12,10 @@ #define SEMU_FEATURE_VIRTIONET 1 #endif +/* ACLINT */ +#ifndef SEMU_FEATURE_ACLINT +#define SEMU_FEATURE_ACLINT 1 +#endif + /* Feature test macro */ #define SEMU_HAS(x) SEMU_FEATURE_##x diff --git a/main.c b/main.c index 39935f89..e47ee2e3 100644 --- a/main.c +++ b/main.c @@ -77,9 +77,23 @@ static void emu_update_timer_interrupt(hart_t *hart) emu_state_t *data = PRIV(hart); /* Sync global timer with local timer */ +#if SEMU_HAS(ACLINT) + hart->time = data->mtimer.mtime; + aclint_mtimer_update_interrupts(hart, &data->mtimer); +#else hart->time = data->clint.mtime; clint_update_interrupts(hart, &data->clint); +#endif +} + +#if SEMU_HAS(ACLINT) +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); } +#endif static void mem_load(hart_t *hart, uint32_t addr, @@ -117,10 +131,26 @@ static void mem_load(hart_t *hart, emu_update_vblk_interrupts(hart->vm); return; #endif +#if SEMU_HAS(ACLINT) + 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; +#else case 0x43: /* clint */ clint_read(hart, &data->clint, addr & 0xFFFFF, width, value); clint_update_interrupts(hart, &data->clint); return; +#endif } } vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val); @@ -162,10 +192,26 @@ static void mem_store(hart_t *hart, emu_update_vblk_interrupts(hart->vm); return; #endif +#if SEMU_HAS(ACLINT) + 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; +#else case 0x43: /* clint */ clint_write(hart, &data->clint, addr & 0xFFFFF, width, value); clint_update_interrupts(hart, &data->clint); return; +#endif } } vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val); @@ -185,9 +231,15 @@ 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: +#if SEMU_HAS(ACLINT) + data->mtimer.mtimecmp[hart->mhartid] = + (((uint64_t) hart->x_regs[RV_R_A1]) << 32) | + (uint64_t) (hart->x_regs[RV_R_A0]); +#else data->clint.mtimecmp[hart->mhartid] = (((uint64_t) hart->x_regs[RV_R_A1]) << 32) | (uint64_t) (hart->x_regs[RV_R_A0]); +#endif hart->sip &= ~RV_INT_STI_BIT; return (sbi_ret_t){SBI_SUCCESS, 0}; default: @@ -262,11 +314,19 @@ static inline sbi_ret_t handle_sbi_ecall_IPI(hart_t *hart, int32_t fid) 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++) { +#if SEMU_HAS(ACLINT) + data->sswi.ssip[i] = 1; +#else data->clint.msip[i] = 1; +#endif } } else { for (int i = hart_mask_base; hart_mask; hart_mask >>= 1, i++) { +#if SEMU_HAS(ACLINT) + data->sswi.ssip[i] = hart_mask & 1; +#else data->clint.msip[i] = hart_mask & 1; +#endif } } @@ -526,7 +586,11 @@ static int semu_start(int argc, char **argv) /* Initialize the emulator */ emu_state_t emu; memset(&emu, 0, sizeof(emu)); +#if SEMU_HAS(ACLINT) + semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ); +#else semu_timer_init(&emu.clint.mtime, CLOCK_FREQ); +#endif /* Set up RAM */ emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, @@ -616,6 +680,9 @@ static int semu_start(int argc, char **argv) } emu_update_timer_interrupt(vm.hart[i]); +#if SEMU_HAS(ACLINT) + emu_update_swi_interrupt(vm.hart[i]); +#endif vm_step(vm.hart[i]); if (likely(!vm.hart[i]->error)) diff --git a/riscv.c b/riscv.c index 069aeeb7..c4d71543 100644 --- a/riscv.c +++ b/riscv.c @@ -805,7 +805,11 @@ void vm_step(hart_t *vm) uint8_t idx = ilog2(applicable); if (idx == 1) { emu_state_t *data = PRIV(vm); +#if SEMU_HAS(ACLINT) + data->sswi.ssip[vm->mhartid] = 0; +#else data->clint.msip[vm->mhartid] = 0; +#endif } vm->exc_cause = (1U << 31) | idx; vm->stval = 0; diff --git a/scripts/gen-clint-dts.py b/scripts/gen-clint-dts.py new file mode 100644 index 00000000..22df971d --- /dev/null +++ b/scripts/gen-clint-dts.py @@ -0,0 +1,72 @@ +import sys + +def cpu_template (id): + return f"""cpu{id}: cpu@{id} {{ + device_type = "cpu"; + compatible = "riscv"; + reg = <{id}>; + riscv,isa = "rv32ima"; + mmu-type = "riscv,sv32"; + cpu{id}_intc: interrupt-controller {{ + #interrupt-cells = <1>; + #address-cells = <0>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }}; + }}; + """ + +def cpu_format(nums): + s = "" + for i in range(nums): + s += cpu_template(i) + return s + +def plic_irq_format(nums): + s = "" + for i in range(nums): + s += f"<&cpu{i}_intc 9>, " + return s[:-2] + +def clint_irq_format(nums): + s = "" + for i in range(nums): + s += f"<&cpu{i}_intc 3 &cpu{i}_intc 7>, " + return s[:-2] + +def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq): + return f"""/{{ + cpus {{ + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <{clock_freq}>; + {cpu_list} + }}; + + soc: soc@F0000000 {{ + plic0: interrupt-controller@0 {{ + #interrupt-cells = <1>; + #address-cells = <0>; + compatible = "sifive,plic-1.0.0"; + reg = <0x0000000 0x4000000>; + interrupt-controller; + interrupts-extended = {plic_list}; + riscv,ndev = <31>; + }}; + + clint0: clint@4300000 {{ + compatible = "riscv,clint0"; + interrupts-extended = + {clint_list}; + reg = <0x4300000 0x10000>; + }}; + }}; +}}; +""" + +dtsi = sys.argv[1] +harts = int(sys.argv[2]) +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)) diff --git a/scripts/gen-hart-dts.py b/scripts/gen-hart-dts.py index 22df971d..f02e33d4 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,26 @@ 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>; + interrupt-controller; + interrupts-extended = {sswi_list}; + reg = <0x4500000 0x4000>; + compatible = "riscv,aclint-sswi"; + }}; + + mswi0: mswi@4400000 {{ + #interrupt-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 +96,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)) \ No newline at end of file