[dvsim] Move time tracking into its own class in Deploy.py

Pull the logic out of Deploy and use a timer, rather than repeatedly
adding deltas (which will drift and is more difficult than just doing
it properly!).

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/util/dvsim/Deploy.py b/util/dvsim/Deploy.py
index c55c2b5..9ff96f3 100644
--- a/util/dvsim/Deploy.py
+++ b/util/dvsim/Deploy.py
@@ -15,6 +15,7 @@
 
 from sim_utils import get_cov_summary_table
 from tabulate import tabulate
+from timer import Timer
 from utils import VERBOSE, find_and_substitute_wildcards, run_cmd
 
 
@@ -24,16 +25,13 @@
     """
 
     # Timer in hours, minutes and seconds.
-    hh = 0
-    mm = 0
-    ss = 0
+    timer = Timer()
 
     # Maintain a list of dispatched items.
     dispatch_counter = 0
 
     # Misc common deploy settings.
     print_legend = True
-    print_interval = 5
     max_parallel = 16
     max_odirs = 5
     # Max jobs dispatched in one go.
@@ -466,37 +464,13 @@
                     log.error("%s: Failed to run bkill\n", e)
 
     @staticmethod
-    def increment_timer():
-        # sub function that increments with overflow = 60
-        def _incr_ovf_60(val):
-            if val >= 59:
-                val = 0
-                return val, True
-            else:
-                val += 1
-                return val, False
-
-        incr_hh = False
-        Deploy.ss, incr_mm = _incr_ovf_60(Deploy.ss)
-        if incr_mm:
-            Deploy.mm, incr_hh = _incr_ovf_60(Deploy.mm)
-        if incr_hh:
-            Deploy.hh += 1
-
-    @staticmethod
     def deploy(items):
         dispatched_items = []
         queued_items = []
 
         # Print timer val in hh:mm:ss.
         def get_timer_val():
-            return "%02i:%02i:%02i" % (Deploy.hh, Deploy.mm, Deploy.ss)
-
-        # Check if elapsed time has reached the next print interval.
-        def has_print_interval_reached():
-            # Deploy.print_interval is expected to be < 1 hour.
-            return (((Deploy.mm * 60 + Deploy.ss) %
-                     Deploy.print_interval) == 0)
+            return Deploy.timer.hms()
 
         def dispatch_items(items):
             item_names = OrderedDict()
@@ -627,8 +601,7 @@
             # Advance time by 1s if there is more work to do.
             if not all_done:
                 time.sleep(1)
-                Deploy.increment_timer()
-                print_status_flag = has_print_interval_reached()
+                print_status_flag = Deploy.timer.check_time()
 
 
 class CompileSim(Deploy):
diff --git a/util/dvsim/Timer.py b/util/dvsim/Timer.py
new file mode 100644
index 0000000..4c305c5
--- /dev/null
+++ b/util/dvsim/Timer.py
@@ -0,0 +1,50 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+import time
+
+
+class Timer:
+    '''A timer to keep track of how long jobs have been running
+
+    This has a notion of start time (the time when the object was constructed),
+    together with a time when the results should next be printed.
+
+    '''
+
+    print_interval = 5
+
+    def __init__(self):
+        self.start = time.monotonic()
+        self.next_print = self.start + Timer.print_interval
+
+    def period(self):
+        '''Return the float time in seconds since start'''
+        return time.monotonic() - self.start
+
+    def hms(self):
+        '''Get the time since start in hh:mm:ss'''
+        period = self.period()
+        secs = int(period + 0.5)
+        mins = secs // 60
+        hours = mins // 60
+        return '{:02}:{:02}:{:02}'.format(hours, mins % 60, secs % 60)
+
+    def check_time(self):
+        '''Return true if we have passed next_print.
+
+        If so, increment next_print by print_interval unless the result would
+        be in the past, in which case set it to the current time plus
+        print_interval.
+
+        '''
+        now = time.monotonic()
+        if now < self.next_print:
+            return False
+
+        self.next_print += Timer.print_interval
+        if self.next_print <= now:
+            self.next_print = now + Timer.print_interval
+
+        return True
diff --git a/util/dvsim/dvsim.py b/util/dvsim/dvsim.py
index 753d6ff..dda3ee5 100755
--- a/util/dvsim/dvsim.py
+++ b/util/dvsim/dvsim.py
@@ -31,6 +31,7 @@
 from signal import SIGINT, signal
 
 import Deploy
+import Timer
 import utils
 from CfgFactory import make_cfg
 
@@ -636,7 +637,7 @@
     Deploy.RunTest.fixed_seed = args.fixed_seed
 
     # Register the common deploy settings.
-    Deploy.Deploy.print_interval = args.print_interval
+    Timer.Timer.print_interval = args.print_interval
     Deploy.Deploy.max_parallel = args.max_parallel
     Deploy.Deploy.max_odirs = args.max_odirs