Make the standard time code work on a Teensy

This is a much nicer API to work with.

Change-Id: I5a654e8d953e5b64b76192d5b4ddb947b666d22f
diff --git a/aos/time/BUILD b/aos/time/BUILD
index b76cd99..7fbd0fb 100644
--- a/aos/time/BUILD
+++ b/aos/time/BUILD
@@ -1,5 +1,7 @@
 package(default_visibility = ["//visibility:public"])
 
+load("//tools:environments.bzl", "mcu_cpus")
+
 cc_library(
     name = "time",
     srcs = [
@@ -8,12 +10,24 @@
     hdrs = [
         "time.h",
     ],
+    compatible_with = mcu_cpus,
     deps = [
         "//aos:macros",
-        "//aos/mutex:mutex",
-        "//aos/logging",
-        "//aos/ipc_lib:shared_mem",
-    ],
+        "//aos/type_traits",
+    ] + select({
+        # TODO(Brian): Deduplicate this (both CPU names and values).
+        "//tools:cpu_cortex_m4f": [
+            "//motors/core",
+        ],
+        "//tools:cpu_cortex_m4f_k22": [
+            "//motors/core",
+        ],
+        "//conditions:default": [
+            "//aos/ipc_lib:shared_mem",
+            "//aos/logging",
+            "//aos/mutex",
+        ],
+    }),
 )
 
 cc_test(
@@ -24,7 +38,7 @@
     deps = [
         ":time",
         "//aos/logging",
-        "//aos/util:death_test_log_implementation",
         "//aos/testing:googletest",
+        "//aos/util:death_test_log_implementation",
     ],
 )
diff --git a/aos/time/time.cc b/aos/time/time.cc
index 0d7295c..f585192 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -6,6 +6,8 @@
 #include <atomic>
 #include <chrono>
 
+#ifdef __linux__
+
 // We only use global_core from here, which is weak, so we don't really have a
 // dependency on it.
 #include "aos/ipc_lib/shared_mem.h"
@@ -13,8 +15,19 @@
 #include "aos/logging/logging.h"
 #include "aos/mutex/mutex.h"
 
+#else  // __linux__
+
+#include "motors/core/kinetis.h"
+
+// The systick interrupt increments this every 1ms.
+extern "C" volatile uint32_t systick_millis_count;
+
+#endif  // __linux__
+
 namespace chrono = ::std::chrono;
 
+#ifdef __linux__
+
 namespace std {
 namespace this_thread {
 template <>
@@ -42,10 +55,13 @@
 }  // namespace this_thread
 }  // namespace std
 
+#endif  // __linux__
 
 namespace aos {
 namespace time {
 
+#ifdef __linux__
+
 // State required to enable and use mock time.
 namespace {
 // True if mock time is enabled.
@@ -98,6 +114,8 @@
       chrono::duration_cast<chrono::nanoseconds>(offset).count();
 }
 
+#endif  // __linux__
+
 struct timespec to_timespec(
     const ::aos::monotonic_clock::duration duration) {
   struct timespec time_timespec;
@@ -119,11 +137,11 @@
 constexpr monotonic_clock::time_point monotonic_clock::min_time;
 
 monotonic_clock::time_point monotonic_clock::now() noexcept {
-  {
-    if (time::mock_time_enabled.load(::std::memory_order_relaxed)) {
-      MutexLocker time_mutex_locker(&time::time_mutex);
-      return time::current_mock_time;
-    }
+#ifdef __linux__
+
+  if (time::mock_time_enabled.load(::std::memory_order_relaxed)) {
+    MutexLocker time_mutex_locker(&time::time_mutex);
+    return time::current_mock_time;
   }
 
   struct timespec current_time;
@@ -139,6 +157,32 @@
 
   return time_point(::std::chrono::seconds(current_time.tv_sec) +
                     ::std::chrono::nanoseconds(current_time.tv_nsec)) + offset;
+
+#else  // __linux__
+
+  __disable_irq();
+  const uint32_t current_counter = SYST_CVR;
+  uint32_t ms_count = systick_millis_count;
+  const uint32_t istatus = SCB_ICSR;
+  __enable_irq();
+  // If the interrupt is pending and the timer has already wrapped from 0 back
+  // up to its max, then add another ms.
+  if ((istatus & SCB_ICSR_PENDSTSET) && current_counter > 50) {
+    ++ms_count;
+  }
+
+  // It counts down, but everything we care about counts up.
+  const uint32_t counter_up = ((F_CPU / 1000) - 1) - current_counter;
+
+  // "3.2.1.2 System Tick Timer" in the TRM says "The System Tick Timer's clock
+  // source is always the core clock, FCLK".
+  using systick_duration =
+      std::chrono::duration<uint32_t, std::ratio<1, F_CPU>>;
+
+  return time_point(aos::time::round<std::chrono::nanoseconds>(
+      std::chrono::milliseconds(ms_count) + systick_duration(counter_up)));
+
+#endif  // __linux__
 }
 
 
diff --git a/aos/time/time.h b/aos/time/time.h
index 9bef741..6f69aa7 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -39,6 +39,8 @@
 
 namespace time {
 
+#ifdef __linux__
+
 // Enables returning the mock time value for Now instead of checking the system
 // clock.
 void EnableMockTime(monotonic_clock::time_point now);
@@ -78,6 +80,8 @@
   DISALLOW_COPY_AND_ASSIGN(TimeFreezer);
 };
 
+#endif  // __linux__
+
 // Converts a monotonic_clock::duration into a timespec object.
 struct timespec to_timespec(::aos::monotonic_clock::duration duration);
 
@@ -85,9 +89,72 @@
 // epoch.
 struct timespec to_timespec(::aos::monotonic_clock::time_point time);
 
+namespace time_internal {
+
+template <class T>
+struct is_duration : std::false_type {};
+template <class Rep, class Period>
+struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
+
+}  // namespace time_internal
+
+// Returns the greatest duration t representable in ToDuration that is less or
+// equal to d.
+// Implementation copied from
+// https://en.cppreference.com/w/cpp/chrono/duration/floor.
+// TODO(Brian): Remove once we have C++17 support.
+template <class To, class Rep, class Period,
+          class = std::enable_if_t<time_internal::is_duration<To>{}>>
+constexpr To floor(const std::chrono::duration<Rep, Period> &d) {
+  To t = std::chrono::duration_cast<To>(d);
+  if (t > d) return t - To{1};
+  return t;
+}
+
+// Returns the value t representable in ToDuration that is the closest to d. If
+// there are two such values, returns the even value (that is, the value t such
+// that t % 2 == 0).
+// Implementation copied from
+// https://en.cppreference.com/w/cpp/chrono/duration/round.
+// TODO(Brian): Remove once we have C++17 support.
+template <class To, class Rep, class Period,
+          class = std::enable_if_t<
+              time_internal::is_duration<To>{} &&
+              !std::chrono::treat_as_floating_point<typename To::rep>{}>>
+constexpr To round(const std::chrono::duration<Rep, Period> &d) {
+  To t0 = aos::time::floor<To>(d);
+  To t1 = t0 + To{1};
+  auto diff0 = d - t0;
+  auto diff1 = t1 - d;
+  if (diff0 == diff1) {
+    if (t0.count() & 1) return t1;
+    return t0;
+  } else if (diff0 < diff1) {
+    return t0;
+  }
+  return t1;
+}
+
+// Returns the nearest time point to tp representable in ToDuration, rounding to
+// even in halfway cases, like std::chrono::round in C++17.
+// Implementation copied from
+// https://en.cppreference.com/w/cpp/chrono/time_point/round.
+// TODO(Brian): Remove once we have C++17 support.
+template <class To, class Clock, class FromDuration,
+          class = std::enable_if_t<
+              time_internal::is_duration<To>{} &&
+              !std::chrono::treat_as_floating_point<typename To::rep>{}>>
+constexpr std::chrono::time_point<Clock, To> round(
+    const std::chrono::time_point<Clock, FromDuration> &tp) {
+  return std::chrono::time_point<Clock, To>{
+      aos::time::round<To>(tp.time_since_epoch())};
+}
+
 }  // namespace time
 }  // namespace aos
 
+#ifdef __linux__
+
 namespace std {
 namespace this_thread {
 // Template specialization for monotonic_clock, since we can use clock_nanosleep
@@ -98,5 +165,6 @@
 }  // namespace this_thread
 }  // namespace std
 
+#endif  // __linux__
 
 #endif  // AOS_TIME_H_
diff --git a/aos/type_traits/BUILD b/aos/type_traits/BUILD
index 171bb8d..e5590b0 100644
--- a/aos/type_traits/BUILD
+++ b/aos/type_traits/BUILD
@@ -1,10 +1,13 @@
 package(default_visibility = ["//visibility:public"])
 
+load("//tools:environments.bzl", "mcu_cpus")
+
 cc_library(
     name = "type_traits",
     hdrs = [
         "type_traits.h",
     ],
+    compatible_with = mcu_cpus,
 )
 
 cc_test(
diff --git a/aos/type_traits/type_traits.h b/aos/type_traits/type_traits.h
index f0a2e72..437cb3e 100644
--- a/aos/type_traits/type_traits.h
+++ b/aos/type_traits/type_traits.h
@@ -1,8 +1,6 @@
 #ifndef AOS_TYPE_TRAITS_
 #define AOS_TYPE_TRAITS_
 
-#include <features.h>
-
 #include <type_traits>
 
 namespace aos {