qemu-arm.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Dmitry Osipenko <digetx@gmail.com>
To: QEMU Developers <qemu-devel@nongnu.org>, qemu-arm@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	Peter Crosthwaite <crosthwaitepeter@gmail.com>
Subject: [Qemu-arm] [PATCH v17 01/14] hw/ptimer: Add "wraparound after one period" policy
Date: Sun,  2 Oct 2016 18:53:33 +0300	[thread overview]
Message-ID: <f22a670cf1f4be298b31640cb5f4be1df0f20ab6.1475421224.git.digetx@gmail.com> (raw)
In-Reply-To: <cover.1475421224.git.digetx@gmail.com>
In-Reply-To: <cover.1475421224.git.digetx@gmail.com>

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 <digetx@gmail.com>
---
 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.9.3


  reply	other threads:[~2016-10-02 16:20 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-02 15:53 [Qemu-arm] [PATCH v17 00/14] PTimer fixes/features and ARM MPTimer conversion Dmitry Osipenko
2016-10-02 15:53 ` Dmitry Osipenko [this message]
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 02/14] tests: ptimer: Add tests for "wraparound after one period" policy Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 03/14] hw/ptimer: Add "continuous trigger" policy Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 04/14] tests: ptimer: Add tests for " Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 05/14] hw/ptimer: Add "no immediate " Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 06/14] tests: ptimer: Add tests for " Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-arm] [PATCH v17 07/14] hw/ptimer: Add "no immediate reload" policy Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 08/14] tests: ptimer: Add tests for " Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 09/14] hw/ptimer: Add "no counter round down" policy Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 10/14] tests: ptimer: Add tests for " Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 11/14] tests: ptimer: Change the copyright comment Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 12/14] tests: ptimer: Replace 10000 with 1 Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-devel] [PATCH v17 13/14] arm_mptimer: Convert to use ptimer Dmitry Osipenko
2016-10-02 15:53 ` [Qemu-arm] [PATCH v17 14/14] tests: Add tests for the ARM MPTimer Dmitry Osipenko
2016-10-24 12:23 ` [Qemu-arm] [PATCH v17 00/14] PTimer fixes/features and ARM MPTimer conversion Peter Maydell
2016-10-24 18:19   ` Dmitry Osipenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f22a670cf1f4be298b31640cb5f4be1df0f20ab6.1475421224.git.digetx@gmail.com \
    --to=digetx@gmail.com \
    --cc=crosthwaitepeter@gmail.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).