pw_chrono: Improve SystemClock C API

Changes the SystemClock C Api to:
1) Use a pw_chrono_SystemClock_Duration struct instead of aliasing
   an int64_t under pw_chrono_SystemClock_TickCount which could
   accidentally permit direct tick usage.
2) Add PW_SYSTEM_CLOCK_{MS,S,MIN,H} and
   PW_SYSTEM_CLOCK_{MS,S,MIN,H}_CEIL to permit C API users to
   create durations which round up to the nearest tick for deadlines
   and timeouts, mirroring std::chrono::ceil.
3) Add PW_SYSTEM_CLOCK_{MS,S,MIN,H}_FLOOR to permit C API users to
   create durations which round down to the nearest tick for oddball
   corner cases, mirroring std::chrono::floor.
4) In order to enable said macros, the system_clock_config.h backend
   config was changed to require the clock period as a preprocessor
   defines instead of a std::ratio<>.
5) Renames pw_chrono_SystemClock_TimeDelta to
   pw_chrono_SystemClock_TimeElapsed to make the argument ordering
   make more sense.
6) Changes existing std::chrono::duration_cast usage to
   std::chrono::ceil and std::chrono:floor to set a good example
   to be explicit on rounding.
7) Renames pw_chrono_SystemClock_TickCountsToNsTruncate accordingly to
   pw_chrono_SystemClock_DurationToNsFloor.

Requires: pigweed-internal:9340
Change-Id: Ia628dceac53f964eda7c4aacd3d790b8b0e92207
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/31280
Commit-Queue: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_chrono/BUILD b/pw_chrono/BUILD
index b13b14f..7bc077b 100644
--- a/pw_chrono/BUILD
+++ b/pw_chrono/BUILD
@@ -36,6 +36,7 @@
 pw_cc_library(
     name = "system_clock_facade",
     hdrs = [
+        "public/pw_chrono/internal/system_clock_macros.h",
         "public/pw_chrono/system_clock.h",
     ],
     includes = ["public"],
diff --git a/pw_chrono/BUILD.gn b/pw_chrono/BUILD.gn
index 9eda0d7..8ed15db 100644
--- a/pw_chrono/BUILD.gn
+++ b/pw_chrono/BUILD.gn
@@ -32,7 +32,10 @@
 pw_facade("system_clock") {
   backend = pw_chrono_SYSTEM_CLOCK_BACKEND
   public_configs = [ ":public_include_path" ]
-  public = [ "public/pw_chrono/system_clock.h" ]
+  public = [
+    "public/pw_chrono/internal/system_clock_macros.h",
+    "public/pw_chrono/system_clock.h",
+  ]
   public_deps = [
     ":epoch",
     "$dir_pw_preprocessor",
diff --git a/pw_chrono/public/pw_chrono/internal/system_clock_macros.h b/pw_chrono/public/pw_chrono/internal/system_clock_macros.h
new file mode 100644
index 0000000..623ec9b
--- /dev/null
+++ b/pw_chrono/public/pw_chrono/internal/system_clock_macros.h
@@ -0,0 +1,61 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#define _PW_SYSTEM_CLOCK_DURATION(num_ticks) \
+  ((pw_chrono_SystemClock_Duration){.ticks = (num_ticks)})
+
+// clang-format off
+
+// ticks_ceil = ((count * clock_period_den + time_unit_num - 1) * time_unit_den) /
+//              (clock_period_num * time_unit_num)
+#define _PW_SYSTEM_CLOCK_TIME_TO_DURATION_CEIL(                                                                                                    \
+    count, time_unit_seconds_numerator, time_unit_seconds_denominator)                                                                             \
+  _PW_SYSTEM_CLOCK_DURATION(                                                                                                                       \
+      (((int64_t)(count) * PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR + time_unit_seconds_numerator - 1) * time_unit_seconds_denominator) / \
+       (PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR * time_unit_seconds_numerator))
+
+// ticks_floor = (count * clock_period_den * time_unit_den) /
+//               (clock_period_num * time_unit_num)
+#define _PW_SYSTEM_CLOCK_TIME_TO_DURATION_FLOOR(                                                               \
+    count, time_unit_seconds_numerator, time_unit_seconds_denominator)                                         \
+  _PW_SYSTEM_CLOCK_DURATION(                                                                                   \
+      ((int64_t)(count) * PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR * time_unit_seconds_denominator) / \
+      (PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR * time_unit_seconds_numerator))
+
+
+#define PW_SYSTEM_CLOCK_MS_CEIL(milliseconds) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_CEIL(milliseconds, 1000, 1)
+#define PW_SYSTEM_CLOCK_S_CEIL(seconds) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_CEIL(seconds,         1, 1)
+#define PW_SYSTEM_CLOCK_MIN_CEIL(minutes) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_CEIL(minutes,         1, 60)
+#define PW_SYSTEM_CLOCK_H_CEIL(hours) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_CEIL(hours,           1, 60 * 60)
+
+#define PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_FLOOR(milliseconds, 1000, 1)
+#define PW_SYSTEM_CLOCK_S_FLOOR(seconds) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_FLOOR(seconds,         1, 1)
+#define PW_SYSTEM_CLOCK_MIN_FLOOR(minutes) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_FLOOR(minutes,         1, 60)
+#define PW_SYSTEM_CLOCK_H_FLOOR(hours) \
+  _PW_SYSTEM_CLOCK_TIME_TO_DURATION_FLOOR(hours,           1, 60 * 60)
+
+// clang-format on
+
+#define PW_SYSTEM_CLOCK_MS(milliseconds) PW_SYSTEM_CLOCK_MS_CEIL(milliseconds)
+#define PW_SYSTEM_CLOCK_S(seconds) PW_SYSTEM_CLOCK_S_CEIL(seconds)
+#define PW_SYSTEM_CLOCK_MIN(minutes) PW_SYSTEM_CLOCK_MIN_CEIL(minutes)
+#define PW_SYSTEM_CLOCK_H(hours) PW_SYSTEM_CLOCK_H_CEIL(hours)
diff --git a/pw_chrono/public/pw_chrono/system_clock.h b/pw_chrono/public/pw_chrono/system_clock.h
index 36481c2..39093e2 100644
--- a/pw_chrono/public/pw_chrono/system_clock.h
+++ b/pw_chrono/public/pw_chrono/system_clock.h
@@ -18,19 +18,21 @@
 
 #include "pw_preprocessor/util.h"
 
-#ifdef __cplusplus
-
-#include <chrono>
-
 // The backend implements this header to provide the following SystemClock
 // parameters, for more detail on the parameters see the SystemClock usage of
 // them below:
-//   std::ratio<> typed pw::chrono::backend::SystemClockPeriodSecondsRatio type
+//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
+//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
 //   constexpr pw::chrono::Epoch pw::chrono::backend::kSystemClockEpoch;
 //   constexpr bool pw::chrono::backend::kSystemClockFreeRunning;
 //   constexpr bool pw::chrono::backend::kSystemClockNmiSafe;
 #include "pw_chrono_backend/system_clock_config.h"
 
+#ifdef __cplusplus
+
+#include <chrono>
+#include <ratio>
+
 namespace pw::chrono {
 namespace backend {
 
@@ -72,7 +74,8 @@
 struct SystemClock {
   using rep = int64_t;
   // The period must be provided by the backend.
-  using period = backend::SystemClockPeriodSecondsRatio;
+  using period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR,
+                            PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
   using duration = std::chrono::duration<rep, period>;
   using time_point = std::chrono::time_point<SystemClock>;
   // The epoch must be provided by the backend.
@@ -152,9 +155,36 @@
 
 PW_EXTERN_C_START
 
-typedef int64_t pw_chrono_SystemClock_TickCount;
+// C API Users should not create pw_chrono_SystemClock_Duration's directly,
+// instead it is strongly recommended to use macros which express the duration
+// in time units, instead of non-portable ticks.
+//
+// The following macros round up just like std::chrono::ceil, this is the
+// recommended rounding to maintain the "at least" contract of timeouts and
+// deadlines (note the *_CEIL macros are the same only more explicit):
+//   PW_SYSTEM_CLOCK_MS(milliseconds)
+//   PW_SYSTEM_CLOCK_S(seconds)
+//   PW_SYSTEM_CLOCK_MIN(minutes)
+//   PW_SYSTEM_CLOCK_H(hours)
+//   PW_SYSTEM_CLOCK_MS_CEIL(milliseconds)
+//   PW_SYSTEM_CLOCK_S_CEIL(seconds)
+//   PW_SYSTEM_CLOCK_MIN_CEIL(minutes)
+//   PW_SYSTEM_CLOCK_H_CEIL(hours)
+//
+// The following macros round down like std::chrono::{floor,duration_cast},
+// these are discouraged but sometimes necessary:
+//   PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds)
+//   PW_SYSTEM_CLOCK_S_FLOOR(seconds)
+//   PW_SYSTEM_CLOCK_MIN_FLOOR(minutes)
+//   PW_SYSTEM_CLOCK_H_FLOOR(hours)
+#include "pw_chrono/internal/system_clock_macros.h"
+
 typedef struct {
-  pw_chrono_SystemClock_TickCount ticks_since_epoch;
+  int64_t ticks;
+} pw_chrono_SystemClock_Duration;
+
+typedef struct {
+  pw_chrono_SystemClock_Duration duration_since_epoch;
 } pw_chrono_SystemClock_TimePoint;
 typedef int64_t pw_chrono_SystemClock_Nanoseconds;
 
@@ -162,18 +192,18 @@
 pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now();
 
 // Returns the change in time between the current_time - last_time.
-pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_TimeDelta(
+pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed(
     pw_chrono_SystemClock_TimePoint last_time,
     pw_chrono_SystemClock_TimePoint current_time);
 
 // For lossless time unit conversion, the seconds per tick ratio that is
-// numerator/denominator should be used.
-int32_t pw_chrono_SystemClock_PeriodSeconds_Numerator();
-int32_t pw_chrono_SystemClock_PeriodSeconds_Denominator();
+// numerator/denominator should be used:
+//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
+//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
 
-// Warning, this may be lossy due to the use of std::chrono::duration_cast,
+// Warning, this may be lossy due to the use of std::chrono::floor,
 // rounding towards zero.
-pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_TickCountToNsTruncate(
-    pw_chrono_SystemClock_TickCount ticks);
+pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_DurationToNsFloor(
+    pw_chrono_SystemClock_Duration duration);
 
 PW_EXTERN_C_END
diff --git a/pw_chrono/simulated_system_clock_test.cc b/pw_chrono/simulated_system_clock_test.cc
index 5102a99..f7d4fe8 100644
--- a/pw_chrono/simulated_system_clock_test.cc
+++ b/pw_chrono/simulated_system_clock_test.cc
@@ -16,15 +16,16 @@
 
 #include "gtest/gtest.h"
 
+using namespace std::chrono_literals;
+
 namespace pw::chrono {
 namespace {
 
-constexpr auto kArbitraryDuration = std::chrono::hours(42);
 // We can't control the SystemClock's period configuration, so just in case
 // 42 hours cannot be accurately expressed in integer ticks, round the
-// duration w/ duration_cast.
+// duration w/ ceil.
 constexpr auto kRoundedArbitraryDuration =
-    std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
+    std::chrono::ceil<SystemClock::duration>(42h);
 
 TEST(SimulatedSystemClock, InitialTime) {
   SimulatedSystemClock clock;
diff --git a/pw_chrono/system_clock.cc b/pw_chrono/system_clock.cc
index cbf8d45..14a2f28 100644
--- a/pw_chrono/system_clock.cc
+++ b/pw_chrono/system_clock.cc
@@ -32,28 +32,22 @@
 }  // namespace pw::chrono
 
 extern "C" pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now() {
-  return {.ticks_since_epoch =
-              pw::chrono::SystemClock::now().time_since_epoch().count()};
+  return {
+      .duration_since_epoch = {
+          .ticks = pw::chrono::SystemClock::now().time_since_epoch().count()}};
 }
 
-extern "C" pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_TimeDelta(
+extern "C" pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed(
     pw_chrono_SystemClock_TimePoint last_time,
     pw_chrono_SystemClock_TimePoint current_time) {
-  return current_time.ticks_since_epoch - last_time.ticks_since_epoch;
-}
-
-extern "C" int32_t pw_chrono_SystemClock_PeriodSeconds_Numerator() {
-  return pw::chrono::SystemClock::period::num;
-}
-
-extern "C" int32_t pw_chrono_SystemClock_PeriodSeconds_Denominator() {
-  return pw::chrono::SystemClock::period::den;
+  return {.ticks = current_time.duration_since_epoch.ticks -
+                   last_time.duration_since_epoch.ticks};
 }
 
 extern "C" pw_chrono_SystemClock_Nanoseconds
-pw_chrono_SystemClock_TickCountToNsTruncate(
-    pw_chrono_SystemClock_TickCount ticks) {
-  return std::chrono::duration_cast<std::chrono::nanoseconds>(
-             pw::chrono::SystemClock::duration(ticks))
+pw_chrono_SystemClock_DurationToNsFloor(
+    pw_chrono_SystemClock_Duration duration) {
+  return std::chrono::floor<std::chrono::nanoseconds>(
+             pw::chrono::SystemClock::duration(duration.ticks))
       .count();
 }
diff --git a/pw_chrono/system_clock_facade_test.cc b/pw_chrono/system_clock_facade_test.cc
index 1d60b11..af17a65 100644
--- a/pw_chrono/system_clock_facade_test.cc
+++ b/pw_chrono/system_clock_facade_test.cc
@@ -18,6 +18,8 @@
 #include "pw_chrono/system_clock.h"
 #include "pw_preprocessor/util.h"
 
+using namespace std::chrono_literals;
+
 namespace pw::chrono {
 namespace {
 
@@ -25,16 +27,12 @@
 
 // Functions defined in system_clock_facade_test_c.c which call the API from C.
 pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_CallNow();
-pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_CallTimeDelta(
+pw_chrono_SystemClock_Duration pw_chrono_SystemClock_CallTimeElapsed(
     pw_chrono_SystemClock_TimePoint last_time,
     pw_chrono_SystemClock_TimePoint current_time);
 
-int32_t pw_chrono_SystemClock_PeriodSeconds_CallNumerator();
-int32_t pw_chrono_SystemClock_PeriodSeconds_CallDenominator();
-
-pw_chrono_SystemClock_Nanoseconds
-pw_chrono_SystemClock_CallTickCountToNsTruncate(
-    pw_chrono_SystemClock_TickCount ticks);
+pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_CallDurationToNsFloor(
+    pw_chrono_SystemClock_Duration ticks);
 
 }  // extern "C"
 
@@ -81,8 +79,8 @@
   // Verify the clock moves forward.
   bool clock_moved_forward = false;
   for (uint64_t i = 0; i < kMaxIterations; ++i) {
-    if (pw_chrono_SystemClock_CallNow().ticks_since_epoch >
-        start_time.ticks_since_epoch) {
+    if (pw_chrono_SystemClock_CallNow().duration_since_epoch.ticks >
+        start_time.duration_since_epoch.ticks) {
       clock_moved_forward = true;
       break;
     }
@@ -90,32 +88,26 @@
   EXPECT_TRUE(clock_moved_forward);
 }
 
-TEST(SystemClock, TimeDeltaInC) {
+TEST(SystemClock, TimeElapsedInC) {
   const pw_chrono_SystemClock_TimePoint first = pw_chrono_SystemClock_CallNow();
   const pw_chrono_SystemClock_TimePoint last = pw_chrono_SystemClock_CallNow();
   static_assert(SystemClock::is_monotonic);
-  EXPECT_GE(0, pw_chrono_SystemClock_CallTimeDelta(last, first));
-}
-
-TEST(SystemClock, PeriodRatioInC) {
-  EXPECT_EQ(SystemClock::period::num,
-            pw_chrono_SystemClock_PeriodSeconds_CallNumerator());
-  EXPECT_EQ(SystemClock::period::den,
-            pw_chrono_SystemClock_PeriodSeconds_CallDenominator());
+  EXPECT_GE(0, pw_chrono_SystemClock_CallTimeElapsed(last, first).ticks);
 }
 
 TEST(SystemClock, DurationCastInC) {
-  static constexpr auto kArbitraryPeriod = std::chrono::hours(42);
   // We can't control the SystemClock's period configuration, so just in case
   // 42 hours cannot be accurately expressed in integer ticks, round the
-  // duration w/ duration_cast.
+  // duration w/ floor.
   static constexpr auto kRoundedArbitraryDuration =
-      std::chrono::duration_cast<SystemClock::duration>(kArbitraryPeriod);
-  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(
-                kRoundedArbitraryDuration)
-                .count(),
-            pw_chrono_SystemClock_CallTickCountToNsTruncate(
-                kRoundedArbitraryDuration.count()));
+      std::chrono::floor<SystemClock::duration>(42h);
+  static constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
+      PW_SYSTEM_CLOCK_H_FLOOR(42);
+  EXPECT_EQ(
+      std::chrono::floor<std::chrono::nanoseconds>(kRoundedArbitraryDuration)
+          .count(),
+      pw_chrono_SystemClock_CallDurationToNsFloor(
+          kRoundedArbitraryDurationInC));
 }
 
 }  // namespace
diff --git a/pw_chrono/system_clock_facade_test_c.c b/pw_chrono/system_clock_facade_test_c.c
index 1ae3456..117d6d7 100644
--- a/pw_chrono/system_clock_facade_test_c.c
+++ b/pw_chrono/system_clock_facade_test_c.c
@@ -21,22 +21,13 @@
   return pw_chrono_SystemClock_Now();
 }
 
-pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_CallTimeDelta(
+pw_chrono_SystemClock_Duration pw_chrono_SystemClock_CallTimeElapsed(
     pw_chrono_SystemClock_TimePoint last_time,
     pw_chrono_SystemClock_TimePoint current_time) {
-  return pw_chrono_SystemClock_TimeDelta(last_time, current_time);
+  return pw_chrono_SystemClock_TimeElapsed(last_time, current_time);
 }
 
-int32_t pw_chrono_SystemClock_PeriodSeconds_CallNumerator() {
-  return pw_chrono_SystemClock_PeriodSeconds_Numerator();
-}
-
-int32_t pw_chrono_SystemClock_PeriodSeconds_CallDenominator() {
-  return pw_chrono_SystemClock_PeriodSeconds_Denominator();
-}
-
-pw_chrono_SystemClock_Nanoseconds
-pw_chrono_SystemClock_CallTickCountToNsTruncate(
-    pw_chrono_SystemClock_TickCount ticks) {
-  return pw_chrono_SystemClock_TickCountToNsTruncate(ticks);
+pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_CallDurationToNsFloor(
+    pw_chrono_SystemClock_Duration ticks) {
+  return pw_chrono_SystemClock_DurationToNsFloor(ticks);
 }
diff --git a/pw_chrono_freertos/public/pw_chrono_freertos/system_clock_config.h b/pw_chrono_freertos/public/pw_chrono_freertos/system_clock_config.h
index 348392e..7f8d631 100644
--- a/pw_chrono_freertos/public/pw_chrono_freertos/system_clock_config.h
+++ b/pw_chrono_freertos/public/pw_chrono_freertos/system_clock_config.h
@@ -13,16 +13,18 @@
 // the License.
 #pragma once
 
-#include <ratio>
-
 #include "FreeRTOS.h"
+
+// Use the FreeRTOS config's tick rate.
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR 1
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR configTICK_RATE_HZ
+
+#ifdef __cplusplus
+
 #include "pw_chrono/epoch.h"
 
 namespace pw::chrono::backend {
 
-// Use the FreeRTOS config's tick rate.
-using SystemClockPeriodSecondsRatio = std::ratio<1, configTICK_RATE_HZ>;
-
 // The FreeRTOS clock starts at zero during initialization, approximately the
 // time since boot.
 constexpr inline Epoch kSystemClockEpoch = pw::chrono::Epoch::kTimeSinceBoot;
@@ -34,3 +36,5 @@
 constexpr inline bool kSystemClockFreeRunning = false;
 
 }  // namespace pw::chrono::backend
+
+#endif  // __cplusplus
diff --git a/pw_chrono_stl/public/pw_chrono_stl/system_clock_config.h b/pw_chrono_stl/public/pw_chrono_stl/system_clock_config.h
index 05b1c71..9766fa2 100644
--- a/pw_chrono_stl/public/pw_chrono_stl/system_clock_config.h
+++ b/pw_chrono_stl/public/pw_chrono_stl/system_clock_config.h
@@ -13,15 +13,19 @@
 // the License.
 #pragma once
 
-#include <chrono>
+// Ideally we'd use std::chrono::steady_clock::period, however this is not
+// something we can expose to the C API. Instead we assume it has nanosecond
+// compatibility and we rely on implicit conversion to tell us at compile time
+// whether this is incompatible.
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR 1
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR 100'000'0000
+
+#ifdef __cplusplus
 
 #include "pw_chrono/epoch.h"
 
 namespace pw::chrono::backend {
 
-// Provide the native std::chrono::steady_clock period.
-using SystemClockPeriodSecondsRatio = std::chrono::steady_clock::period;
-
 // The std::chrono::steady_clock does not have a defined epoch.
 constexpr inline Epoch kSystemClockEpoch = pw::chrono::Epoch::kUnknown;
 
@@ -32,3 +36,5 @@
 constexpr inline bool kSystemClockFreeRunning = true;
 
 }  // namespace pw::chrono::backend
+
+#endif  // __cplusplus
diff --git a/pw_chrono_threadx/public/pw_chrono_threadx/config.h b/pw_chrono_threadx/public/pw_chrono_threadx/config.h
index a4681c5..bfb2109 100644
--- a/pw_chrono_threadx/public/pw_chrono_threadx/config.h
+++ b/pw_chrono_threadx/public/pw_chrono_threadx/config.h
@@ -31,8 +31,10 @@
 #define PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR 100
 #endif  // PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR
 
-static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR >= 1);
-static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR >= 1);
+static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR >= 1,
+              "the numerator must be positive and cannot be fractional");
+static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR >= 1,
+              "the denominator must be positive and cannot be fractional");
 
 // Because the SystemClock::now() implementation requires the user to invoke it
 // more than once per overflow period, the max timeout is set to ensure that
@@ -43,4 +45,5 @@
 #endif  // PW_CHRONO_THREADX_CFG_MAX_TIMEOUT
 
 static_assert((PW_CHRONO_THREADX_CFG_MAX_TIMEOUT > 0) &&
-              (PW_CHRONO_THREADX_CFG_MAX_TIMEOUT <= (TX_WAIT_FOREVER - 1)));
+                  (PW_CHRONO_THREADX_CFG_MAX_TIMEOUT <= (TX_WAIT_FOREVER - 1)),
+              "the timeout must be greater than 0 and less than the sentinel");
diff --git a/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h
index 3e8a676..6ab829a 100644
--- a/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h
+++ b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h
@@ -13,18 +13,20 @@
 // the License.
 #pragma once
 
-#include <ratio>
-
-#include "pw_chrono/epoch.h"
 #include "pw_chrono_threadx/config.h"
 
-namespace pw::chrono::backend {
-
 // ThreadX does not have an API to determine the tick rate/period, instead
 // require the user to specify this through the configuration.
-using SystemClockPeriodSecondsRatio =
-    std::ratio<PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR,
-               PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR \
+  PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR
+#define PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR \
+  PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR
+
+#ifdef __cplusplus
+
+#include "pw_chrono/epoch.h"
+
+namespace pw::chrono::backend {
 
 // The ThreadX clock starts at zero during initialization, approximately the
 // time since boot.
@@ -37,3 +39,5 @@
 constexpr inline bool kSystemClockFreeRunning = false;
 
 }  // namespace pw::chrono::backend
+
+#endif  // __cplusplus
diff --git a/pw_sync/binary_semaphore.cc b/pw_sync/binary_semaphore.cc
index 439504b..5500964 100644
--- a/pw_sync/binary_semaphore.cc
+++ b/pw_sync/binary_semaphore.cc
@@ -33,15 +33,15 @@
 
 extern "C" bool pw_sync_BinarySemaphore_TryAcquireFor(
     pw_sync_BinarySemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least) {
-  return semaphore->try_acquire_for(SystemClock::duration(for_at_least));
+    pw_chrono_SystemClock_Duration for_at_least) {
+  return semaphore->try_acquire_for(SystemClock::duration(for_at_least.ticks));
 }
 
 extern "C" bool pw_sync_BinarySemaphore_TryAcquireUntil(
     pw_sync_BinarySemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least) {
   return semaphore->try_acquire_until(SystemClock::time_point(
-      SystemClock::duration(until_at_least.ticks_since_epoch)));
+      SystemClock::duration(until_at_least.duration_since_epoch.ticks)));
 }
 
 extern "C" ptrdiff_t pw_sync_BinarySemaphore_Max(void) {
diff --git a/pw_sync/binary_semaphore_facade_test.cc b/pw_sync/binary_semaphore_facade_test.cc
index 7c2273c..d5ad28c 100644
--- a/pw_sync/binary_semaphore_facade_test.cc
+++ b/pw_sync/binary_semaphore_facade_test.cc
@@ -19,6 +19,7 @@
 #include "pw_sync/binary_semaphore.h"
 
 using pw::chrono::SystemClock;
+using namespace std::chrono_literals;
 
 namespace pw::sync {
 namespace {
@@ -32,7 +33,7 @@
 bool pw_sync_BinarySemaphore_CallTryAcquire(pw_sync_BinarySemaphore* semaphore);
 bool pw_sync_BinarySemaphore_CallTryAcquireFor(
     pw_sync_BinarySemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least);
+    pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_BinarySemaphore_CallTryAcquireUntil(
     pw_sync_BinarySemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least);
@@ -40,14 +41,13 @@
 
 }  // extern "C"
 
-static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
 // We can't control the SystemClock's period configuration, so just in case
 // duration cannot be accurately expressed in integer ticks, round the
-// duration w/ duration_cast.
-static constexpr auto kRoundedArbitraryDuration =
-    std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
-static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
-    kRoundedArbitraryDuration.count();
+// duration w/ ceil.
+constexpr auto kRoundedArbitraryDuration =
+    std::chrono::ceil<SystemClock::duration>(42ms);
+constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
+    PW_SYSTEM_CLOCK_MS(42);
 
 TEST(BinarySemaphore, EmptyInitialState) {
   BinarySemaphore semaphore;
@@ -130,17 +130,17 @@
   pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
   ASSERT_TRUE(pw_sync_BinarySemaphore_CallTryAcquireFor(
       &semaphore, kRoundedArbitraryDurationInC));
-  pw_chrono_SystemClock_TickCount time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+  pw_chrono_SystemClock_Duration time_elapsed =
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 
   // Ensure it blocks and fails when empty.
   before = pw_chrono_SystemClock_Now();
   EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
       &semaphore, kRoundedArbitraryDurationInC));
   time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 }
 
 TEST(BinarySemaphore, TryAcquireUntilInC) {
@@ -148,18 +148,20 @@
   pw_sync_BinarySemaphore_CallRelease(&semaphore);
 
   pw_chrono_SystemClock_TimePoint deadline;
-  deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
-                               kRoundedArbitraryDurationInC;
+  deadline.duration_since_epoch = {
+      .ticks = pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
+               kRoundedArbitraryDurationInC.ticks,
+  };
   ASSERT_TRUE(
       pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
-  EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 
   // Ensure it blocks and fails when empty.
   EXPECT_FALSE(
       pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
-  EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 }
 
 TEST(BinarySemaphore, MaxInC) {
diff --git a/pw_sync/binary_semaphore_facade_test_c.c b/pw_sync/binary_semaphore_facade_test_c.c
index 5145301..22dc7f6 100644
--- a/pw_sync/binary_semaphore_facade_test_c.c
+++ b/pw_sync/binary_semaphore_facade_test_c.c
@@ -34,7 +34,7 @@
 
 bool pw_sync_BinarySemaphore_CallTryAcquireFor(
     pw_sync_BinarySemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least) {
+    pw_chrono_SystemClock_Duration for_at_least) {
   return pw_sync_BinarySemaphore_TryAcquireFor(semaphore, for_at_least);
 }
 
diff --git a/pw_sync/counting_semaphore.cc b/pw_sync/counting_semaphore.cc
index 9915e5e..1b7ad3f 100644
--- a/pw_sync/counting_semaphore.cc
+++ b/pw_sync/counting_semaphore.cc
@@ -38,15 +38,15 @@
 
 extern "C" bool pw_sync_CountingSemaphore_TryAcquireFor(
     pw_sync_CountingSemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least) {
-  return semaphore->try_acquire_for(SystemClock::duration(for_at_least));
+    pw_chrono_SystemClock_Duration for_at_least) {
+  return semaphore->try_acquire_for(SystemClock::duration(for_at_least.ticks));
 }
 
 extern "C" bool pw_sync_CountingSemaphore_TryAcquireUntil(
     pw_sync_CountingSemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least) {
   return semaphore->try_acquire_until(SystemClock::time_point(
-      SystemClock::duration(until_at_least.ticks_since_epoch)));
+      SystemClock::duration(until_at_least.duration_since_epoch.ticks)));
 }
 
 extern "C" ptrdiff_t pw_sync_CountingSemaphore_Max(void) {
diff --git a/pw_sync/counting_semaphore_facade_test.cc b/pw_sync/counting_semaphore_facade_test.cc
index 3d5b331..b0f1bc8 100644
--- a/pw_sync/counting_semaphore_facade_test.cc
+++ b/pw_sync/counting_semaphore_facade_test.cc
@@ -19,6 +19,7 @@
 #include "pw_sync/counting_semaphore.h"
 
 using pw::chrono::SystemClock;
+using namespace std::chrono_literals;
 
 namespace pw::sync {
 namespace {
@@ -37,7 +38,7 @@
     pw_sync_CountingSemaphore* semaphore);
 bool pw_sync_CountingSemaphore_CallTryAcquireFor(
     pw_sync_CountingSemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least);
+    pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_CountingSemaphore_CallTryAcquireUntil(
     pw_sync_CountingSemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least);
@@ -45,14 +46,13 @@
 
 }  // extern "C"
 
-static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
 // We can't control the SystemClock's period configuration, so just in case
 // duration cannot be accurately expressed in integer ticks, round the
-// duration w/ duration_cast.
-static constexpr auto kRoundedArbitraryDuration =
-    std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
-static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
-    kRoundedArbitraryDuration.count();
+// duration w/ ceil.
+constexpr auto kRoundedArbitraryDuration =
+    std::chrono::ceil<SystemClock::duration>(42ms);
+constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
+    PW_SYSTEM_CLOCK_MS(42);
 
 TEST(CountingSemaphore, EmptyInitialState) {
   CountingSemaphore semaphore;
@@ -160,17 +160,17 @@
   pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
   ASSERT_TRUE(pw_sync_CountingSemaphore_CallTryAcquireFor(
       &semaphore, kRoundedArbitraryDurationInC));
-  pw_chrono_SystemClock_TickCount time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+  pw_chrono_SystemClock_Duration time_elapsed =
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 
   // Ensure it blocks and fails when empty.
   before = pw_chrono_SystemClock_Now();
   EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquireFor(
       &semaphore, kRoundedArbitraryDurationInC));
   time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 }
 
 TEST(CountingSemaphore, TryAcquireUntilInC) {
@@ -178,18 +178,20 @@
   pw_sync_CountingSemaphore_CallRelease(&semaphore);
 
   pw_chrono_SystemClock_TimePoint deadline;
-  deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
-                               kRoundedArbitraryDurationInC;
+  deadline.duration_since_epoch = {
+      .ticks = pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
+               kRoundedArbitraryDurationInC.ticks,
+  };
   ASSERT_TRUE(
       pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
-  EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 
   // Ensure it blocks and fails when empty.
   EXPECT_FALSE(
       pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
-  EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 }
 
 TEST(CountingSemaphore, MaxInC) {
diff --git a/pw_sync/counting_semaphore_facade_test_c.c b/pw_sync/counting_semaphore_facade_test_c.c
index 592df9b..93db6d1 100644
--- a/pw_sync/counting_semaphore_facade_test_c.c
+++ b/pw_sync/counting_semaphore_facade_test_c.c
@@ -41,7 +41,7 @@
 
 bool pw_sync_CountingSemaphore_CallTryAcquireFor(
     pw_sync_CountingSemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least) {
+    pw_chrono_SystemClock_Duration for_at_least) {
   return pw_sync_CountingSemaphore_TryAcquireFor(semaphore, for_at_least);
 }
 
diff --git a/pw_sync/mutex.cc b/pw_sync/mutex.cc
index 85c3c92..416182e 100644
--- a/pw_sync/mutex.cc
+++ b/pw_sync/mutex.cc
@@ -23,13 +23,13 @@
 }
 
 extern "C" bool pw_sync_Mutex_TryLockFor(
-    pw_sync_Mutex* mutex, pw_chrono_SystemClock_TickCount for_at_least) {
-  return mutex->try_lock_for(SystemClock::duration(for_at_least));
+    pw_sync_Mutex* mutex, pw_chrono_SystemClock_Duration for_at_least) {
+  return mutex->try_lock_for(SystemClock::duration(for_at_least.ticks));
 }
 
 extern "C" bool pw_sync_Mutex_TryLockUntil(
     pw_sync_Mutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least) {
   return mutex->try_lock_until(SystemClock::time_point(
-      SystemClock::duration(until_at_least.ticks_since_epoch)));
+      SystemClock::duration(until_at_least.duration_since_epoch.ticks)));
 }
 extern "C" void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex) { mutex->unlock(); }
diff --git a/pw_sync/mutex_facade_test.cc b/pw_sync/mutex_facade_test.cc
index 9cc5bf3..30cef1f 100644
--- a/pw_sync/mutex_facade_test.cc
+++ b/pw_sync/mutex_facade_test.cc
@@ -19,6 +19,7 @@
 #include "pw_sync/mutex.h"
 
 using pw::chrono::SystemClock;
+using namespace std::chrono_literals;
 
 namespace pw::sync {
 namespace {
@@ -29,21 +30,20 @@
 void pw_sync_Mutex_CallLock(pw_sync_Mutex* mutex);
 bool pw_sync_Mutex_CallTryLock(pw_sync_Mutex* mutex);
 bool pw_sync_Mutex_CallTryLockFor(pw_sync_Mutex* mutex,
-                                  pw_chrono_SystemClock_TickCount for_at_least);
+                                  pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_Mutex_CallTryLockUntil(
     pw_sync_Mutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least);
 void pw_sync_Mutex_CallUnlock(pw_sync_Mutex* mutex);
 
 }  // extern "C"
 
-static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
 // We can't control the SystemClock's period configuration, so just in case
 // duration cannot be accurately expressed in integer ticks, round the
-// duration w/ duration_cast.
-static constexpr auto kRoundedArbitraryDuration =
-    std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
-static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
-    kRoundedArbitraryDuration.count();
+// duration w/ ceil.
+constexpr auto kRoundedArbitraryDuration =
+    std::chrono::ceil<SystemClock::duration>(42ms);
+constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
+    PW_SYSTEM_CLOCK_MS(42);
 
 // TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
 
@@ -124,18 +124,17 @@
   pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
   ASSERT_TRUE(
       pw_sync_Mutex_CallTryLockFor(&mutex, kRoundedArbitraryDurationInC));
-  pw_chrono_SystemClock_TickCount time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+  pw_chrono_SystemClock_Duration time_elapsed =
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 
   // TODO(pwbug/291): Ensure it blocks fails to lock when already held.
   // before = pw_chrono_SystemClock_Now();
   // EXPECT_FALSE(
   //     pw_sync_Mutex_CallTryLockFor(&mutex, kRoundedArbitraryDurationInC));
   // time_elapsed =
-  //     pw_chrono_SystemClock_Now().ticks_since_epoch -
-  //     before.ticks_since_epoch;
-  // EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+  //    pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  // EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 
   pw_sync_Mutex_CallUnlock(&mutex);
 }
@@ -143,16 +142,17 @@
 TEST(Mutex, TryLockUnlockUntilInC) {
   pw::sync::Mutex mutex;
   pw_chrono_SystemClock_TimePoint deadline;
-  deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
-                               kRoundedArbitraryDurationInC;
+  deadline.duration_since_epoch.ticks =
+      pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
+      kRoundedArbitraryDurationInC.ticks;
   ASSERT_TRUE(pw_sync_Mutex_CallTryLockUntil(&mutex, deadline));
-  EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 
   // TODO(pwbug/291): Ensure it blocks fails to lock when already held.
   // EXPECT_FALSE(pw_sync_Mutex_CallTryLockUntil(&mutex, deadline));
-  // EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
-  //           deadline.ticks_since_epoch);
+  // EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+  //           deadline.duration_since_epoch.ticks);
 
   mutex.unlock();
 }
diff --git a/pw_sync/mutex_facade_test_c.c b/pw_sync/mutex_facade_test_c.c
index 9004912..5b1e631 100644
--- a/pw_sync/mutex_facade_test_c.c
+++ b/pw_sync/mutex_facade_test_c.c
@@ -25,8 +25,8 @@
   return pw_sync_Mutex_TryLock(mutex);
 }
 
-bool pw_sync_Mutex_CallTryLockFor(
-    pw_sync_Mutex* mutex, pw_chrono_SystemClock_TickCount for_at_least) {
+bool pw_sync_Mutex_CallTryLockFor(pw_sync_Mutex* mutex,
+                                  pw_chrono_SystemClock_Duration for_at_least) {
   return pw_sync_Mutex_TryLockFor(mutex, for_at_least);
 }
 
diff --git a/pw_sync/public/pw_sync/binary_semaphore.h b/pw_sync/public/pw_sync/binary_semaphore.h
index 4886b34..43603ec 100644
--- a/pw_sync/public/pw_sync/binary_semaphore.h
+++ b/pw_sync/public/pw_sync/binary_semaphore.h
@@ -109,7 +109,7 @@
 bool pw_sync_BinarySemaphore_TryAcquire(pw_sync_BinarySemaphore* semaphore);
 bool pw_sync_BinarySemaphore_TryAcquireFor(
     pw_sync_BinarySemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least);
+    pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_BinarySemaphore_TryAcquireUntil(
     pw_sync_BinarySemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least);
diff --git a/pw_sync/public/pw_sync/counting_semaphore.h b/pw_sync/public/pw_sync/counting_semaphore.h
index a7b94f6..8768bab 100644
--- a/pw_sync/public/pw_sync/counting_semaphore.h
+++ b/pw_sync/public/pw_sync/counting_semaphore.h
@@ -111,7 +111,7 @@
 bool pw_sync_CountingSemaphore_TryAcquire(pw_sync_CountingSemaphore* semaphore);
 bool pw_sync_CountingSemaphore_TryAcquireFor(
     pw_sync_CountingSemaphore* semaphore,
-    pw_chrono_SystemClock_TickCount for_at_least);
+    pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_CountingSemaphore_TryAcquireUntil(
     pw_sync_CountingSemaphore* semaphore,
     pw_chrono_SystemClock_TimePoint until_at_least);
diff --git a/pw_sync/public/pw_sync/mutex.h b/pw_sync/public/pw_sync/mutex.h
index 3030c09..1bc77d7 100644
--- a/pw_sync/public/pw_sync/mutex.h
+++ b/pw_sync/public/pw_sync/mutex.h
@@ -109,7 +109,7 @@
 void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex);
 bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex);
 bool pw_sync_Mutex_TryLockFor(pw_sync_Mutex* mutex,
-                              pw_chrono_SystemClock_TickCount for_at_least);
+                              pw_chrono_SystemClock_Duration for_at_least);
 bool pw_sync_Mutex_TryLockUntil(pw_sync_Mutex* mutex,
                                 pw_chrono_SystemClock_TimePoint until_at_least);
 void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex);
diff --git a/pw_thread/public/pw_thread/sleep.h b/pw_thread/public/pw_thread/sleep.h
index 4deb472..93e8320 100644
--- a/pw_thread/public/pw_thread/sleep.h
+++ b/pw_thread/public/pw_thread/sleep.h
@@ -37,7 +37,7 @@
 
 PW_EXTERN_C_START
 
-void pw_this_thread_SleepFor(pw_chrono_SystemClock_TickCount for_at_least);
+void pw_this_thread_SleepFor(pw_chrono_SystemClock_Duration for_at_least);
 void pw_this_thread_SleepUntil(pw_chrono_SystemClock_TimePoint until_at_least);
 
 PW_EXTERN_C_END
diff --git a/pw_thread/sleep.cc b/pw_thread/sleep.cc
index 28313fd..403df3a 100644
--- a/pw_thread/sleep.cc
+++ b/pw_thread/sleep.cc
@@ -17,12 +17,12 @@
 using pw::chrono::SystemClock;
 
 extern "C" void pw_this_thread_SleepFor(
-    pw_chrono_SystemClock_TickCount for_at_least) {
-  pw::this_thread::sleep_for(SystemClock::duration(for_at_least));
+    pw_chrono_SystemClock_Duration for_at_least) {
+  pw::this_thread::sleep_for(SystemClock::duration(for_at_least.ticks));
 }
 
 extern "C" void pw_this_thread_SleepUntil(
     pw_chrono_SystemClock_TimePoint until_at_least) {
   pw::this_thread::sleep_until(SystemClock::time_point(
-      SystemClock::duration(until_at_least.ticks_since_epoch)));
+      SystemClock::duration(until_at_least.duration_since_epoch.ticks)));
 }
diff --git a/pw_thread/sleep_facade_test.cc b/pw_thread/sleep_facade_test.cc
index 5956720..42548bd 100644
--- a/pw_thread/sleep_facade_test.cc
+++ b/pw_thread/sleep_facade_test.cc
@@ -20,6 +20,7 @@
 #include "pw_thread/sleep.h"
 
 using pw::chrono::SystemClock;
+using namespace std::chrono_literals;
 
 namespace pw::this_thread {
 namespace {
@@ -27,20 +28,19 @@
 extern "C" {
 
 // Functions defined in sleep_facade_test_c.c which call the API from C.
-void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_TickCount for_at_least);
+void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_Duration for_at_least);
 void pw_this_thread_CallSleepUntil(
     pw_chrono_SystemClock_TimePoint until_at_least);
 
 }  // extern "C"
 
-static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
 // We can't control the SystemClock's period configuration, so just in case
 // duration cannot be accurately expressed in integer ticks, round the
-// duration w/ duration_cast.
-static constexpr auto kRoundedArbitraryDuration =
-    std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
-static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
-    kRoundedArbitraryDuration.count();
+// duration w/ ceil.
+constexpr auto kRoundedArbitraryDuration =
+    std::chrono::ceil<SystemClock::duration>(42ms);
+constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
+    PW_SYSTEM_CLOCK_MS(42);
 
 TEST(Sleep, SleepFor) {
   // Ensure we are in a thread context, meaning we are permitted to sleep.
@@ -68,9 +68,9 @@
 
   pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
   pw_this_thread_SleepFor(kRoundedArbitraryDurationInC);
-  pw_chrono_SystemClock_TickCount time_elapsed =
-      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
-  EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+  pw_chrono_SystemClock_Duration time_elapsed =
+      pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
+  EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
 }
 
 TEST(Sleep, SleepUntilInC) {
@@ -78,11 +78,12 @@
   ASSERT_NE(get_id(), thread::Id());
 
   pw_chrono_SystemClock_TimePoint deadline;
-  deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
-                               kRoundedArbitraryDurationInC;
+  deadline.duration_since_epoch.ticks =
+      pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
+      kRoundedArbitraryDurationInC.ticks;
   pw_this_thread_CallSleepUntil(deadline);
-  EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
-            deadline.ticks_since_epoch);
+  EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
+            deadline.duration_since_epoch.ticks);
 }
 
 }  // namespace
diff --git a/pw_thread/sleep_facade_test_c.c b/pw_thread/sleep_facade_test_c.c
index c2f536e..450c387 100644
--- a/pw_thread/sleep_facade_test_c.c
+++ b/pw_thread/sleep_facade_test_c.c
@@ -17,7 +17,7 @@
 
 #include "pw_thread/sleep.h"
 
-void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_TickCount for_at_least) {
+void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_Duration for_at_least) {
   pw_this_thread_SleepFor(for_at_least);
 }
 
diff --git a/pw_trace/example/sample_app.cc b/pw_trace/example/sample_app.cc
index c0fd0ea..8edefa9 100644
--- a/pw_trace/example/sample_app.cc
+++ b/pw_trace/example/sample_app.cc
@@ -38,7 +38,7 @@
 auto start = system_clock::now();
 uint32_t GetTimeSinceBootMillis() {
   auto delta = system_clock::now() - start;
-  return duration_cast<milliseconds>(delta).count();
+  return floor<milliseconds>(delta).count();
 }
 
 // Creating a very simple runnable with predictable behaviour to help with the
@@ -208,4 +208,4 @@
 
 }  // namespace
 
-void RunTraceSampleApp() { StartFakeKernel(); }
\ No newline at end of file
+void RunTraceSampleApp() { StartFakeKernel(); }
diff --git a/pw_trace_tokenized/host_trace_time.cc b/pw_trace_tokenized/host_trace_time.cc
index ed004dc..ce60880 100644
--- a/pw_trace_tokenized/host_trace_time.cc
+++ b/pw_trace_tokenized/host_trace_time.cc
@@ -29,7 +29,7 @@
 // Define trace time as a counter for tests.
 PW_TRACE_TIME_TYPE pw_trace_GetTraceTime() {
   auto delta = steady_clock::now() - start;
-  return duration_cast<microseconds>(delta).count();
+  return floor<microseconds>(delta).count();
 }
 
 // Microsecond time source