From: Sebastian Huber <sebastian.hu...@embedded-brains.de>

---
 cpukit/score/src/kern_tc.c                | 25 ++++++++++++++++++++---
 testsuites/sptests/sptimecounter01/init.c |  4 ++--
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c
index 52ae6aa034..00645600a1 100644
--- a/cpukit/score/src/kern_tc.c
+++ b/cpukit/score/src/kern_tc.c
@@ -158,7 +158,7 @@ struct timehands {
        struct timecounter      *th_counter;
        int64_t                 th_adjustment;
        uint64_t                th_scale;
-       u_int                   th_large_delta;
+       uint32_t                th_large_delta;
        uint32_t                th_offset_count;
        struct bintime          th_offset;
        struct bintime          th_bintime;
@@ -218,6 +218,7 @@ static struct timehands th0 = {
        .th_counter = &dummy_timecounter,
        .th_scale = (uint64_t)-1 / 1000000,
        .th_offset = { .sec = 1 },
+       .th_large_delta = 1000000,
        .th_generation = UINT_MAX,
 #ifdef __rtems__
        .th_bintime = { .sec = TOD_SECONDS_1970_THROUGH_1988 },
@@ -373,7 +374,12 @@ bintime_off(struct bintime *bt, u_int off)
        struct timehands *th;
        struct bintime *btp;
        uint64_t scale, x;
+#ifndef __rtems__
        u_int delta, gen, large_delta;
+#else /* __rtems__ */
+       uint32_t delta, large_delta;
+       u_int gen;
+#endif /* __rtems__ */
 
        do {
                th = timehands;
@@ -536,17 +542,30 @@ sbintime_t
 _Timecounter_Sbinuptime(void)
 {
        struct timehands *th;
-       uint32_t gen;
        sbintime_t sbt;
+       uint64_t scale;
+       uint32_t delta;
+       uint32_t large_delta;
+       u_int gen;
 
        do {
                th = timehands;
                gen = atomic_load_acq_int(&th->th_generation);
                sbt = bttosbt(th->th_offset);
-               sbt += (th->th_scale * tc_delta(th)) >> 32;
+               scale = th->th_scale;
+               delta = tc_delta(th);
+               large_delta = th->th_large_delta;
                atomic_thread_fence_acq();
        } while (gen == 0 || gen != th->th_generation);
 
+       if (__predict_false(delta >= large_delta)) {
+               /* Avoid overflow for scale * delta. */
+               sbt += (scale >> 32) * delta;
+               sbt += ((scale & 0xffffffff) * delta) >> 32;
+       } else {
+               sbt += (scale * delta) >> 32;
+       }
+
        return (sbt);
 }
 #endif /* __rtems__ */
diff --git a/testsuites/sptests/sptimecounter01/init.c 
b/testsuites/sptests/sptimecounter01/init.c
index 81b705473e..12d5a7820d 100644
--- a/testsuites/sptests/sptimecounter01/init.c
+++ b/testsuites/sptests/sptimecounter01/init.c
@@ -196,11 +196,11 @@ void boot_card(const char *cmdline)
   assert(bt.sec == 1);
   assert(bt.frac == 18446744073708);
 
-  /* Ensure that the fraction overflows and the second remains constant */
+  /* Check that a large delta yields a correct time */
   ctx->counter = (0xf0000000 | 1) + TEST_FREQ;
   rtems_bsd_binuptime(&bt);
   assert(ctx->counter == (0xf0000000 | 2) + TEST_FREQ);
-  assert(bt.sec == 1);
+  assert(bt.sec == 2);
   assert(bt.frac == 18446742522092);
 
   test_install(ctx);
-- 
2.17.1

_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to