From 4e7f1faa3a9955295a4a5e965df059a3226514b7 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Tue, 17 Jun 2025 16:03:24 +0700 Subject: [PATCH 1/3] hw/timer/renesas_cmt: Update renesas_cmt timer Correct CMCNT read value calculation Signed-off-by: Duy Nguyen Signed-off-by: Phi Tran --- hw/timer/renesas_cmt.c | 97 +++++++++++++++------------------- include/hw/timer/renesas_cmt.h | 1 + 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index 93e7f58cc200d..53de3c2aff104 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -51,56 +51,44 @@ REG16(CMCR, 0) REG16(CMCNT, 2) REG16(CMCOR, 4) -static void update_events(RCMTState *cmt, int ch) -{ - int64_t next_time; +static uint16_t read_cmcnt(RCMTState *cmt, int ch) { + if (!(cmt->cmstr & (1 << ch))) { + return cmt->cmcnt[ch]; + } + int64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - cmt->tick[ch]; + uint64_t ticks = (elapsed_ns * cmt->cmt_freq[ch]) / (NANOSECONDS_PER_SECOND); + uint32_t limit = cmt->cmcor[ch] + 1; + return (cmt->cmcnt[ch] + ticks) % limit; +} - if ((cmt->cmstr & (1 << ch)) == 0) { - /* count disable, so not happened next event. */ +static void update_events(RCMTState *cmt, int ch) { + if (!(cmt->cmstr & (1 << ch))) { return; } - next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; - next_time *= NANOSECONDS_PER_SECOND; - next_time /= cmt->input_freq; - /* - * CKS -> div rate - * 0 -> 8 (1 << 3) - * 1 -> 32 (1 << 5) - * 2 -> 128 (1 << 7) - * 3 -> 512 (1 << 9) - */ - next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); - next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - timer_mod(&cmt->timer[ch], next_time); -} + uint16_t current = read_cmcnt(cmt, ch); + uint32_t remain = (cmt->cmcor[ch] + 1) - current; + if (remain == 0) { + remain = cmt->cmcor[ch] + 1; + } + int64_t next_time = (int64_t)remain * NANOSECONDS_PER_SECOND / cmt->cmt_freq[ch]; -static int64_t read_cmcnt(RCMTState *cmt, int ch) -{ - int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t fire_time = now + next_time; - if (cmt->cmstr & (1 << ch)) { - delta = (now - cmt->tick[ch]); - delta /= NANOSECONDS_PER_SECOND; - delta /= cmt->input_freq; - delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); - cmt->tick[ch] = now; - return cmt->cmcnt[ch] + delta; - } else { - return cmt->cmcnt[ch]; + if (fire_time <= now) { + fire_time = now + 1; } + + timer_mod(&cmt->timer[ch], fire_time); } static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) { RCMTState *cmt = opaque; int ch = offset / 0x08; - uint64_t ret; if (offset == A_CMSTR) { - ret = 0; - ret = FIELD_DP16(ret, CMSTR, STR, - FIELD_EX16(cmt->cmstr, CMSTR, STR)); - return ret; + return FIELD_EX16(cmt->cmstr, CMSTR, STR); } else { offset &= 0x07; if (ch == 0) { @@ -108,12 +96,7 @@ static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) } switch (offset) { case A_CMCR: - ret = 0; - ret = FIELD_DP16(ret, CMCR, CKS, - FIELD_EX16(cmt->cmstr, CMCR, CKS)); - ret = FIELD_DP16(ret, CMCR, CMIE, - FIELD_EX16(cmt->cmstr, CMCR, CMIE)); - return ret; + return cmt->cmcr[ch]; case A_CMCNT: return read_cmcnt(cmt, ch); case A_CMCOR: @@ -123,12 +106,13 @@ static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " "not implemented\n", offset); - return UINT64_MAX; + return UINT16_MAX; } -static void start_stop(RCMTState *cmt, int ch, int st) -{ - if (st) { +static void start_stop(RCMTState *cmt, int ch, int enable) { + if (enable) { + cmt->cmcnt[ch] = 0; + cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); update_events(cmt, ch); } else { timer_del(&cmt->timer[ch]); @@ -155,9 +139,13 @@ static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) FIELD_EX16(val, CMCR, CKS)); cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE, FIELD_EX16(val, CMCR, CMIE)); + cmt->cmt_freq[ch] = + cmt->input_freq / (8u << (2 * FIELD_EX16(cmt->cmcr[ch], CMCR, CKS))); + update_events(cmt, ch); break; case 2: cmt->cmcnt[ch] = val; + cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); break; case 4: cmt->cmcor[ch] = val; @@ -216,26 +204,29 @@ static void rcmt_reset(DeviceState *dev) { RCMTState *cmt = RCMT(dev); cmt->cmstr = 0; - cmt->cmcr[0] = cmt->cmcr[1] = 0; - cmt->cmcnt[0] = cmt->cmcnt[1] = 0; - cmt->cmcor[0] = cmt->cmcor[1] = 0xffff; + for (int i = 0; i < CMT_CH; i++) { + cmt->cmcr[i] = 0; + cmt->cmcnt[i] = 0; + cmt->cmcor[i] = 0xFFFF; + cmt->tick[i] = 0; + } } static void rcmt_init(Object *obj) { SysBusDevice *d = SYS_BUS_DEVICE(obj); RCMTState *cmt = RCMT(obj); - int i; memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops, cmt, "renesas-cmt", 0x10); sysbus_init_mmio(d, &cmt->memory); - for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) { + for (int i = 0; i < CMT_CH; i++) { sysbus_init_irq(d, &cmt->cmi[i]); + cmt->cmt_freq[i] = cmt->input_freq >> 3; + timer_init_ns(&cmt->timer[i], QEMU_CLOCK_VIRTUAL, + (i == 0 ? timer_event0 : timer_event1), cmt); } - timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt); - timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt); } static const VMStateDescription vmstate_rcmt = { diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h index 1c0b65c1d5a70..3d7a3c624f5b4 100644 --- a/include/hw/timer/renesas_cmt.h +++ b/include/hw/timer/renesas_cmt.h @@ -29,6 +29,7 @@ struct RCMTState { /*< public >*/ uint64_t input_freq; + uint64_t cmt_freq[CMT_CH]; MemoryRegion memory; uint16_t cmstr; From 9b2cc108fdfea090e0cc5b7ba1e7e9f9fca96f51 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Tue, 17 Jun 2025 16:40:22 +0700 Subject: [PATCH 2/3] target/rx: Update flow of interrupt execution for target rx do_interrupt function is call directly from CPU exception so we need to add condition to check for CPU exception in case of hardware interrupt is preempted to prevent unexpect behavior Signed-off-by: Duy Nguyen --- target/rx/helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/rx/helper.c b/target/rx/helper.c index e8aabf40ffb41..60958ad032cec 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -48,7 +48,6 @@ void rx_cpu_do_interrupt(CPUState *cs) uint32_t save_psw; env->in_sleep = 0; - if (env->psw_u) { env->usp = env->regs[0]; } else { @@ -56,8 +55,9 @@ void rx_cpu_do_interrupt(CPUState *cs) } save_psw = rx_cpu_pack_psw(env); env->psw_pm = env->psw_i = env->psw_u = 0; + int32_t vec = cs->exception_index; - if (do_irq) { + if (do_irq && vec < 0) { if (do_irq & CPU_INTERRUPT_FIR) { env->bpc = env->pc; env->bpsw = save_psw; @@ -79,7 +79,6 @@ void rx_cpu_do_interrupt(CPUState *cs) "interrupt 0x%02x raised\n", env->ack_irq); } } else { - uint32_t vec = cs->exception_index; const char *expname = "unknown exception"; env->isp -= 4; From fe132ed0c9ae14a56bbf94b1d0d16b029b671c62 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Fri, 20 Jun 2025 17:36:26 +0700 Subject: [PATCH 3/3] target/rx: Reset the CPU at qemu reset time This ensure that the CPU gets reset every time QEMU resets. Signed-off-by: Keith Packard Signed-off-by: Duy Nguyen --- target/rx/cpu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 0ba0d55ab5bdb..5a412746db5c2 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -27,6 +27,7 @@ #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" +#include "system/reset.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { @@ -129,6 +130,13 @@ static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) return oc; } +static void rx_cpu_reset(void *opaque) +{ + RXCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + static void rx_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -142,7 +150,7 @@ static void rx_cpu_realize(DeviceState *dev, Error **errp) } qemu_init_vcpu(cs); - cpu_reset(cs); + qemu_register_reset(rx_cpu_reset, RX_CPU(cs)); rcc->parent_realize(dev, errp); }