utils: general purpose utilities

Also includes a new README.md explaining the code structure to come.

Change-Id: I2725308c3f0dc2cbd88f5f8481555154223e4e97
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a6aad0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,47 @@
+# Trace Based Model (TBM)
+
+## Code structure
+
+### Executables:
+
+- gentrace-spike.py - reads a Spike trace and reformat it. We expect to support
+  other functional simulators, each of those will have its own gentrace-*.py
+  file.
+- tbm.py - runs a trace in TBM; the main tool here.
+- merge-counters.py - merges results from multiple runs of TBM.
+
+### Python modules:
+
+**NOTE:** currently TBM includes a single model for each of the building block
+units. The intention is that other models will be added in the future to cover
+uArchs that are not supported by the current models. interfaces.py defines (what
+we expect to be) the API of the building blocks.
+
+- counter.py - performance counters.
+- cpu.py - defines CPU, a cpu model (includes instances of FetchUnit, SchedUnit,
+  ExecUnit, and MemorySystem).
+- disassembler.py - bits we need to elaborate Spike traces.
+- exec_unit.py - defines ExecUnit, an execution unit model (includes instances of
+  ScalarPipe, VectorPipe, scoreboard.Preemptive and scoreboard.VecPreemptive).
+- fetch_unit.py - defines FetchUnit.
+- instruction.fbs - FlatBuffer schema for the Instruction data class (used for
+  saving elaborated traces). The FBInstruction.Instruction module is generated
+  from this file.
+- instruction.py - defines Instruction, a data class representing a single
+  instruction instance in the trace.
+- interfaces.py - defines the internal API. This will be more important when we
+  add different models (i.e. implementations) for the various units.
+- memory_system.py - defines MemorySystem, a main memory and cache hierarchy model.
+- queue.py - defines Queue, FIFO queue model.
+- scalar_pipe.py - defines ScalarPipe, a scalar functional unit model.
+- sched_unit.py - defines SchedUnit, an issue queue model.
+- scoreboard.py - defines Preemptive and VecPreemptive, scoreboard models.
+- tbm_options.py - command line parsing for tbm.py.
+- trace.py - reads a trace (as generated by gentrace-*.py).
+- utilities.py - general purpose constructs.
+- vector_pipe.py - defines VectorPipe, a vector functional unit model.
+
+### Other files:
+
+- config/uarch.schema.json - JSON schema for uArch configuration files.
+- config/default_arch.yaml - a uArch configuration example.
diff --git a/utilities.py b/utilities.py
new file mode 100644
index 0000000..4fdbff3
--- /dev/null
+++ b/utilities.py
@@ -0,0 +1,77 @@
+"""Collection of general purpose utilities."""
+
+import enum
+import itertools
+import logging
+import sys
+import threading
+import time
+from typing import Any, Callable, Iterable, Sequence
+
+
+def logging_config(level: int) -> None:
+    """Configure the root logger to track events starting from `level`.
+
+    If tracked, WARNING events (and above) go to stderr, other events go to
+    stdout.
+    """
+    # Log messages below WARNING go to stdout.
+    stdout_h = logging.StreamHandler(sys.stdout)
+    stdout_h.setLevel(logging.DEBUG)
+    stdout_h.addFilter(lambda record: record.levelno < logging.WARNING)
+
+    # Log messages that are WARNING and above go to stderr.
+    stderr_h = logging.StreamHandler(sys.stderr)
+    stderr_h.setLevel(logging.WARNING)
+
+    formatter = logging.Formatter("[%(asctime)s] %(message)s",
+                                  datefmt="%H:%M:%S'")
+    stdout_h.setFormatter(formatter)
+    stderr_h.setFormatter(formatter)
+
+    logging.basicConfig(handlers=[stdout_h, stderr_h],
+                        level=level)
+
+
+class FileFormat(enum.Enum):
+    FLATBUFFERS = enum.auto()
+    JSON = enum.auto()
+
+
+def flatten(xss: Iterable[Iterable[Any]]) -> Sequence[Any]:
+    return list(itertools.chain.from_iterable(xss))
+
+
+class CallEvery():
+    """Call some function every few seconds.
+
+    `with CallEvery(s, f) as c: ...` calls `f()` every `s` seconds, while inside
+    the with block.
+    """
+
+    def __init__(self, period, f: Callable[[], None]):
+        self.period = period
+        self.f = f
+        self.running = False
+        self.timer = None
+        self.t = None
+
+    def __enter__(self) -> None:
+        self.running = True
+        self.t = time.time() + self.period
+        self.timer = threading.Timer(max(self.t - time.time(), 0), self._run)
+        self.timer.start()
+
+    def __exit__(self, exc_type, exc_value, traceback) -> bool:
+        self.running = False
+        if self.timer:
+            self.timer.cancel()
+        return False
+
+    def _run(self):
+        if self.running:
+            self.f()
+            self.t += self.period
+            self.timer = threading.Timer(max(self.t - time.time(), 0),
+                                         self._run)
+            self.timer.start()