Hi Peter,

thanks for the review. I have addressed all three comments:

- hit_time is now local to stm32f2xx_timer_set_alarm();
- CNT is preserved and returned verbatim while the timer is stopped, including 
when it is greater than ARR;
- both VMState version fields are now 2, and stopped_cnt is serialized directly.

I have also added a qtest for the stopped CNT case (ARR = 100, CNT = 200), and 
all five timer subtests pass.

The updated [PATCH v2 1/2] is attached.

Thanks,
Manuele

Ing. Manuele Conti
Software Engineer - Research & Development department
Archimede Energia


P: +390323586222
F: +390323583066
M: +393783081660
A: Via dell'Industria 15, 28924 Verbania Fondotoce (VB)
W: https://www.archimede-energia.com
E: [email protected]

[cid:[email protected]]




Please consider the environment before printing this e-mail.
This e-mail message including any attachments is for the sole use of the 
intended recipient and may contain confidential and privileged information 
pursuant to Legislative Decree 196/2003 and the European General Data 
Protection Regulation 679/2016 – GDPR-. Any unauthorized review, use, 
disclosure or distribution is prohibited. If you are not the intended 
recipient, please contact the sender by reply e-mail and destroy all copies of 
the original message.

On 6 Oct 2026, at 14:56, Peter Maydell <[email protected]> wrote:

Attenzione: Questa e` la prima volta che ricevi un'email da questo mittente.
Attenzione: Assicurati che sia qualcuno di cui ti fidi.

On Wed, 23 Sept 2026 at 22:56, Manuele Conti
<[email protected]> wrote:

RM0090 counters count from 0 to ARR inclusive, with period ARR + 1
for all ARR values including ARR == 0. Fix set_alarm to wrap CNT,
arm an absolute expiry and delete the QEMU timer when CEN is cleared.

Freeze CNT while CEN == 0 in stopped_cnt so a TIM_CNT write
programmed while stopped is preserved and resumed on enable, and
reset deletes any pending timer event.

VMState goes to version 2 to serialize stopped_cnt. Loading a v1
stream leaves stopped_cnt at 0, so a timer stopped with CNT != 0
loads CNT as 0; no post_load workaround. The pre-existing QEMUTimer
expiry limit is unchanged.

Add qtests covering PSC/ARR/CEN/UIF, CNT <= ARR, CNT preset before
CEN, ARR == 0, stop freeze on CEN=0, restart, and no spurious IRQ
after reset.

Signed-off-by: Manuele Conti <[email protected]>
---
MAINTAINERS                        |   1 +
hw/timer/stm32f2xx_timer.c         | 101 ++++++++++++++++----
include/hw/timer/stm32f2xx_timer.h |   7 ++
tests/qtest/meson.build            |   4 +
tests/qtest/stm32f405-timer-test.c | 142 +++++++++++++++++++++++++++++
5 files changed, 236 insertions(+), 19 deletions(-)
create mode 100644 tests/qtest/stm32f405-timer-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5bf895732c..f1b3d75cc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1213,6 +1213,7 @@ F: hw/misc/stm32f4xx_syscfg.c
F: hw/misc/stm32f4xx_exti.c
F: hw/misc/stm32_rcc.c
F: include/hw/misc/stm32_rcc.h
+F: tests/qtest/stm32f405*

Netduino 2
M: Alistair Francis <[email protected]>
diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c
index 0442ac64ad..1639037130 100644
--- a/hw/timer/stm32f2xx_timer.c
+++ b/hw/timer/stm32f2xx_timer.c
@@ -53,7 +53,8 @@ static void stm32f2xx_timer_interrupt(void *opaque)
    if (s->tim_dier & TIM_DIER_UIE && s->tim_cr1 & TIM_CR1_CEN) {
        s->tim_sr |= 1;
        qemu_irq_pulse(s->irq);
-        stm32f2xx_timer_set_alarm(s, s->hit_time);
+        stm32f2xx_timer_set_alarm(s,
+                                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));

This change means that the only uses of s->hit_time are in the
sequence in stm32f2xx_timer_set_alarm():
   s->hit_time = (uint64_t)now + delta_ns;
   timer_mod(s->timer, s->hit_time);
   DB_PRINT("Wait Time: %" PRId64 " ticks\n", s->hit_time);

That means we can change hit_time to be a simple local variable
in that function; it doesn't need to be a state field any more.

    }

    if (s->tim_ccmr1 & (TIM_CCMR1_OC2M2 | TIM_CCMR1_OC2M1) &&

static uint64_t stm32f2xx_timer_read(void *opaque, hwaddr offset,
@@ -148,8 +172,11 @@ static uint64_t stm32f2xx_timer_read(void *opaque, hwaddr 
offset,
    case TIM_CCER:
        return s->tim_ccer;
    case TIM_CNT:
-        return stm32f2xx_ns_to_ticks(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) 
-
-               s->tick_offset;
+        if (!(s->tim_cr1 & TIM_CR1_CEN)) {
+            return s->stopped_cnt % ((uint64_t)s->tim_arr + 1);
+        }

This looks odd -- I wouldn't expect the auto-reload register to have
any effect if the counter is stopped. That is, if you do:
* set the counter to disabled
* set ARR to 100
* set CNT to 200
* read CNT

I would expect to read back 200 (and then if you enable the counter
it probably rolls back to 0 on the next clock, regardless of
what its value above the auto-reload value is?)

Do you have real hardware to confirm the behaviour against?

+        return stm32f2xx_timer_get_cnt(s,
+                   qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
    case TIM_PSC:
        return s->tim_psc;
    case TIM_ARR:

@@ -272,7 +328,7 @@ static const MemoryRegionOps stm32f2xx_timer_ops = {

static const VMStateDescription vmstate_stm32f2xx_timer = {
    .name = TYPE_STM32F2XX_TIMER,
-    .version_id = 1,
+    .version_id = 2,
    .minimum_version_id = 1,
    .fields = (const VMStateField[]) {
        VMSTATE_INT64(tick_offset, STM32F2XXTimerState),
@@ -294,6 +350,13 @@ static const VMStateDescription vmstate_stm32f2xx_timer = {
        VMSTATE_UINT32(tim_dcr, STM32F2XXTimerState),
        VMSTATE_UINT32(tim_dmar, STM32F2XXTimerState),
        VMSTATE_UINT32(tim_or, STM32F2XXTimerState),
+        /*
+         * New in version 2: frozen CNT while CEN == 0. Pre-existing
+         * limitation left unchanged: QEMUTimer expiry (hit_time) is
+         * not migrated and is re-armed by the next register write;
+         * see stm32f2xx_timer_set_alarm.
+         */
+        VMSTATE_UINT32_V(stopped_cnt, STM32F2XXTimerState, 2),

We don't care about migration compatibility on these machine types,
so we could just bump both version_id and minimum_version_id to 2
and define the field as a simple VMSTATE_UINT32().

        VMSTATE_END_OF_LIST()
    }
};

Otherwise this looks OK, and certainly better than what we have now.

thanks
-- PMM

--
Questo messaggio e' stato analizzato da MyAntispam.it ed e' risultato non 
infetto.
Seguire il link qui sotto per segnalarlo come spam:
http://email.myantispam.it/action/4hzbqW3ZTDzMqgR/report-as-bad
http://email.myantispam.it/action/4hzbqW3ZTDzMqgR/blocklist


Attachment: 0001-hw-timer-stm32f2xx_timer-fix-periodic-mode-and-CEN-h.patch
Description: 0001-hw-timer-stm32f2xx_timer-fix-periodic-mode-and-CEN-h.patch

Reply via email to