[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 08/32] hw/ptimer: Add "wraparound after one period" p
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 08/32] hw/ptimer: Add "wraparound after one period" policy |
Date: |
Mon, 24 Oct 2016 18:25:04 +0100 |
From: Dmitry Osipenko <address@hidden>
Currently, periodic counter wraps around immediately once counter reaches
"0", this is wrong behaviour for some of the timers, resulting in one period
being lost. Add new ptimer policy that provides correct behaviour for such
timers, so that counter stays with "0" for a one period before wrapping
around.
Signed-off-by: Dmitry Osipenko <address@hidden>
Message-id: address@hidden
Reviewed-by: Peter Maydell <address@hidden>
Signed-off-by: Peter Maydell <address@hidden>
---
hw/core/ptimer.c | 58 +++++++++++++++++++++++++++++++++++++++--------------
include/hw/ptimer.h | 4 ++++
2 files changed, 47 insertions(+), 15 deletions(-)
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index c45c835..1f4122d 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -13,6 +13,8 @@
#include "sysemu/replay.h"
#include "sysemu/qtest.h"
+#define DELTA_ADJUST 1
+
struct ptimer_state
{
uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
@@ -35,16 +37,17 @@ static void ptimer_trigger(ptimer_state *s)
}
}
-static void ptimer_reload(ptimer_state *s)
+static void ptimer_reload(ptimer_state *s, int delta_adjust)
{
uint32_t period_frac = s->period_frac;
uint64_t period = s->period;
+ uint64_t delta = s->delta;
- if (s->delta == 0) {
+ if (delta == 0) {
ptimer_trigger(s);
- s->delta = s->limit;
+ delta = s->delta = s->limit;
}
- if (s->delta == 0 || s->period == 0) {
+ if (delta == 0 || s->period == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
}
@@ -53,6 +56,10 @@ static void ptimer_reload(ptimer_state *s)
return;
}
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ delta += delta_adjust;
+ }
+
/*
* Artificially limit timeout rate to something
* achievable under QEMU. Otherwise, QEMU spends all
@@ -62,15 +69,15 @@ static void ptimer_reload(ptimer_state *s)
* on the current generation of host machines.
*/
- if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) {
- period = 10000 / s->delta;
+ if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
+ period = 10000 / delta;
period_frac = 0;
}
s->last_event = s->next_event;
- s->next_event = s->last_event + s->delta * period;
+ s->next_event = s->last_event + delta * period;
if (period_frac) {
- s->next_event += ((int64_t)period_frac * s->delta) >> 32;
+ s->next_event += ((int64_t)period_frac * delta) >> 32;
}
timer_mod(s->timer, s->next_event);
}
@@ -83,7 +90,7 @@ static void ptimer_tick(void *opaque)
if (s->enabled == 2) {
s->enabled = 0;
} else {
- ptimer_reload(s);
+ ptimer_reload(s, DELTA_ADJUST);
}
}
@@ -94,6 +101,7 @@ uint64_t ptimer_get_count(ptimer_state *s)
if (s->enabled) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t next = s->next_event;
+ int64_t last = s->last_event;
bool expired = (now - next >= 0);
bool oneshot = (s->enabled == 2);
@@ -118,7 +126,7 @@ uint64_t ptimer_get_count(ptimer_state *s)
/* We need to divide time by period, where time is stored in
rem (64-bit integer) and period is stored in period/period_frac
(64.32 fixed point).
-
+
Doing full precision division is hard, so scale values and
do a 64-bit division. The result should be rounded down,
so that the rounding error never causes the timer to go
@@ -145,6 +153,26 @@ uint64_t ptimer_get_count(ptimer_state *s)
div += 1;
}
counter = rem / div;
+
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ /* Before wrapping around, timer should stay with counter = 0
+ for a one period. */
+ if (!oneshot && s->delta == s->limit) {
+ if (now == last) {
+ /* Counter == delta here, check whether it was
+ adjusted and if it was, then right now it is
+ that "one period". */
+ if (counter == s->limit + DELTA_ADJUST) {
+ return 0;
+ }
+ } else if (counter == s->limit) {
+ /* Since the counter is rounded down and now != last,
+ the counter == limit means that delta was adjusted
+ by +1 and right now it is that adjusted period. */
+ return 0;
+ }
+ }
+ }
}
} else {
counter = s->delta;
@@ -157,7 +185,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count)
s->delta = count;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -174,7 +202,7 @@ void ptimer_run(ptimer_state *s, int oneshot)
s->enabled = oneshot ? 2 : 1;
if (was_disabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -198,7 +226,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
s->period_frac = 0;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -210,7 +238,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq)
s->period_frac = (1000000000ll << 32) / freq;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -223,7 +251,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int
reload)
s->delta = limit;
if (s->enabled && reload) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 26c7fdc..03441cb 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -35,6 +35,10 @@
*/
#define PTIMER_POLICY_DEFAULT 0
+/* Periodic timer counter stays with "0" for a one period before wrapping
+ * around. */
+#define PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD (1 << 0)
+
/* ptimer.c */
typedef struct ptimer_state ptimer_state;
typedef void (*ptimer_cb)(void *opaque);
--
2.7.4
- [Qemu-devel] [PULL 06/32] target-arm: Make page size a runtime setting, (continued)
- [Qemu-devel] [PULL 06/32] target-arm: Make page size a runtime setting, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 25/32] i2c: Fix SMBus read transactions to avoid double events, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 26/32] timer: stm32f2xx_timer: add check for prescaler value, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 31/32] target-arm: Implement new HLT trap for semihosting, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 18/32] tests: ptimer: Change the copyright comment, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 10/32] hw/ptimer: Add "continuous trigger" policy, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 21/32] tests: Add tests for the ARM MPTimer, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 13/32] tests: ptimer: Add tests for "no immediate trigger" policy, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 02/32] exec.c: Remove static allocation of sub_section of sub_page, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 28/32] hw/arm: QOM'ify pxa2xx_gpio.c, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 08/32] hw/ptimer: Add "wraparound after one period" policy,
Peter Maydell <=
- [Qemu-devel] [PULL 05/32] migration/savevm.c: migrate non-default page size, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 19/32] tests: ptimer: Replace 10000 with 1, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 17/32] tests: ptimer: Add tests for "no counter round down" policy, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 15/32] tests: ptimer: Add tests for "no immediate reload" policy, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 32/32] i2c: Add asserts for second smbus i2c_start_transfer(), Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 09/32] tests: ptimer: Add tests for "wraparound after one period" policy, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 04/32] cpu: Support a target CPU having a variable page size, Peter Maydell, 2016/10/24
- [Qemu-devel] [PULL 03/32] translate-all.c: Compute L1 page table properties at runtime, Peter Maydell, 2016/10/24
- Re: [Qemu-devel] [PULL 00/32] target-arm queue, no-reply, 2016/10/24
- Re: [Qemu-devel] [PULL 00/32] target-arm queue, Peter Maydell, 2016/10/25