pw_chrono: Adds initial SystemClock support to Pigweed
Adds the initial pw_chrono module to Pigweed with a facade for the
pw::chrono::SystemClock. In adition, the pw_chrono_stl module is
provided with the first STL based backend of said module by using
std::chrono::steady_clock.
Change-Id: Id29f4280aa399a81518f6d13183cf495b2ee72dc
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/23260
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Ewout van Bekkum <ewout@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 75fd40a..f3957e4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -218,6 +218,7 @@
"$dir_pw_blob_store",
"$dir_pw_bytes",
"$dir_pw_checksum",
+ "$dir_pw_chrono",
"$dir_pw_cpu_exception",
"$dir_pw_hdlc_lite",
"$dir_pw_metric",
@@ -257,6 +258,7 @@
"$dir_pw_blob_store:tests",
"$dir_pw_bytes:tests",
"$dir_pw_checksum:tests",
+ "$dir_pw_chrono:tests",
"$dir_pw_containers:tests",
"$dir_pw_cpu_exception_armv7m:tests",
"$dir_pw_fuzzer:tests",
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 4ad9ea4..29b0061 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -60,6 +60,8 @@
"$dir_pw_build:docs",
"$dir_pw_bytes:docs",
"$dir_pw_checksum:docs",
+ "$dir_pw_chrono:docs",
+ "$dir_pw_chrono_stl:docs",
"$dir_pw_cli:docs",
"$dir_pw_containers:docs",
"$dir_pw_cpu_exception:docs",
diff --git a/modules.gni b/modules.gni
index b079b63..d7dd1a3 100644
--- a/modules.gni
+++ b/modules.gni
@@ -29,6 +29,8 @@
dir_pw_build = get_path_info("pw_build", "abspath")
dir_pw_bytes = get_path_info("pw_bytes", "abspath")
dir_pw_checksum = get_path_info("pw_checksum", "abspath")
+ dir_pw_chrono = get_path_info("pw_chrono", "abspath")
+ dir_pw_chrono_stl = get_path_info("pw_chrono_stl", "abspath")
dir_pw_cli = get_path_info("pw_cli", "abspath")
dir_pw_containers = get_path_info("pw_containers", "abspath")
dir_pw_cpu_exception = get_path_info("pw_cpu_exception", "abspath")
diff --git a/pw_chrono/BUILD b/pw_chrono/BUILD
new file mode 100644
index 0000000..b13b14f
--- /dev/null
+++ b/pw_chrono/BUILD
@@ -0,0 +1,100 @@
+# 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.
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_library",
+ "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache License 2.0
+
+# TODO(pwbug/101): Need to add support for facades/backends to Bazel.
+PW_CHRONO_SYSTEM_CLOCK_BACKEND = "//pw_chrono_stl:system_clock"
+
+pw_cc_library(
+ name = "epoch",
+ hdrs = [
+ "public/pw_chrono/epoch.h",
+ ],
+ includes = ["public"],
+)
+
+pw_cc_library(
+ name = "system_clock_facade",
+ hdrs = [
+ "public/pw_chrono/system_clock.h",
+ ],
+ includes = ["public"],
+ srcs = [
+ "system_clock.cc"
+ ],
+ deps = [
+ ":epoch",
+ PW_CHRONO_SYSTEM_CLOCK_BACKEND + "_headers",
+ "//pw_preprocessor",
+ ],
+)
+
+pw_cc_library(
+ name = "system_clock",
+ deps = [
+ ":system_clock_facade",
+ PW_CHRONO_SYSTEM_CLOCK_BACKEND + "_headers",
+ ],
+)
+
+pw_cc_library(
+ name = "system_clock_backend",
+ deps = [
+ PW_CHRONO_SYSTEM_CLOCK_BACKEND,
+ ],
+)
+
+pw_cc_library(
+ name = "simulated_system_clock",
+ hdrs = [
+ "public/pw_chrono/simulated_system_clock.h",
+ ],
+ deps = [
+ ":system_clock",
+ "//pw_sync:spin_lock",
+ ],
+)
+
+pw_cc_test(
+ name = "simulated_system_clock_test",
+ srcs = [
+ "simulated_system_clock_test.cc",
+ ],
+ deps = [
+ ":simulated_system_clock",
+ "//pw_unit_test",
+ ],
+)
+
+pw_cc_test(
+ name = "system_clock_facade_test",
+ srcs = [
+ "system_clock_facade_test.cc",
+ "system_clock_facade_test_c.c",
+ ],
+ deps = [
+ ":system_clock",
+ "//pw_preprocessor",
+ "//pw_unit_test",
+ ],
+)
diff --git a/pw_chrono/BUILD.gn b/pw_chrono/BUILD.gn
new file mode 100644
index 0000000..9eda0d7
--- /dev/null
+++ b/pw_chrono/BUILD.gn
@@ -0,0 +1,81 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/facade.gni")
+import("$dir_pw_chrono/backend.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+config("public_include_path") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+}
+
+pw_source_set("epoch") {
+ public = [ "public/pw_chrono/epoch.h" ]
+ public_configs = [ ":public_include_path" ]
+}
+
+pw_facade("system_clock") {
+ backend = pw_chrono_SYSTEM_CLOCK_BACKEND
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_chrono/system_clock.h" ]
+ public_deps = [
+ ":epoch",
+ "$dir_pw_preprocessor",
+ ]
+ sources = [ "system_clock.cc" ]
+}
+
+# Dependency injectable implementation of pw::chrono::SystemClock::Interface.
+pw_source_set("simulated_system_clock") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_chrono/simulated_system_clock.h" ]
+ public_deps = [
+ ":system_clock",
+ "$dir_pw_sync:spin_lock",
+ ]
+}
+
+pw_test_group("tests") {
+ tests = [
+ ":simulated_system_clock_test",
+ ":system_clock_facade_test",
+ ]
+}
+
+pw_test("simulated_system_clock_test") {
+ enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != ""
+ sources = [ "simulated_system_clock_test.cc" ]
+ deps = [ ":simulated_system_clock" ]
+}
+
+pw_test("system_clock_facade_test") {
+ enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != ""
+ sources = [
+ "system_clock_facade_test.cc",
+ "system_clock_facade_test_c.c",
+ ]
+ deps = [
+ ":system_clock",
+ "$dir_pw_preprocessor",
+ pw_chrono_SYSTEM_CLOCK_BACKEND,
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_chrono/backend.gni b/pw_chrono/backend.gni
new file mode 100644
index 0000000..f5f80e8
--- /dev/null
+++ b/pw_chrono/backend.gni
@@ -0,0 +1,18 @@
+# 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.
+
+declare_args() {
+ # Backend for the pw_chrono module's system_clock.
+ pw_chrono_SYSTEM_CLOCK_BACKEND = ""
+}
diff --git a/pw_chrono/docs.rst b/pw_chrono/docs.rst
new file mode 100644
index 0000000..e62aba6
--- /dev/null
+++ b/pw_chrono/docs.rst
@@ -0,0 +1,21 @@
+.. _module-pw_chrono:
+
+---------
+pw_chrono
+---------
+Pigweed's chrono module provides facilities for applications to deal with time,
+leveraging many pieces of STL's the ``std::chrono`` library but with a focus
+on portability for constrained embedded devices and maintaining correctness.
+
+.. warning::
+ This module is under construction, not ready for use, and the documentation
+ is incomplete.
+
+SystemClock facade
+------------------
+The ``pw::chrono::SystemClock`` is meant to serve as the clock used for time
+bound operations such as thread sleeping, waiting on mutexes/semaphores, etc.
+The ``SystemClock`` always uses a signed 64 bit as the underlying type for time
+points and durations. This means users do not have to worry about clock overflow
+risk as long as rational durations and time points as used, i.e. within a range
+of ±292 years.
diff --git a/pw_chrono/public/pw_chrono/epoch.h b/pw_chrono/public/pw_chrono/epoch.h
new file mode 100644
index 0000000..5f0e860
--- /dev/null
+++ b/pw_chrono/public/pw_chrono/epoch.h
@@ -0,0 +1,40 @@
+// 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
+
+namespace pw::chrono {
+
+enum class Epoch {
+ // The epoch is unkown and possibly even undefined in case the clock is not
+ // always enabled and the epoch may reset over time.
+ kUnknown,
+
+ kTimeSinceBoot,
+
+ // Time since 00:00:00 UTC, Thursday, 1 January 1970, including leap seconds.
+ kUtcWallClock,
+
+ // Time since 00:00:00, 6 January 1980 UTC. Leap seconds are not inserted into
+ // GPS. Thus, every time a leap second is inserted into UTC, UTC falls another
+ // second behind GPS.
+ kGpsWallClock,
+
+ // Time since 00:00:00, 1 January 1958, and is offset 10 seconds ahead of UTC
+ // at that date (i.e., its epoch, 1958-01-01 00:00:00 TAI, is 1957-12-31
+ // 23:59:50 UTC). Leap seconds are not inserted into TAI. Thus, every time a
+ // leap second is inserted into UTC, UTC falls another second behind TAI.
+ kTaiWallClock,
+};
+
+} // namespace pw::chrono
diff --git a/pw_chrono/public/pw_chrono/simulated_system_clock.h b/pw_chrono/public/pw_chrono/simulated_system_clock.h
new file mode 100644
index 0000000..477b34a
--- /dev/null
+++ b/pw_chrono/public/pw_chrono/simulated_system_clock.h
@@ -0,0 +1,70 @@
+// 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
+
+#include <mutex>
+
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/spin_lock.h"
+
+namespace pw::chrono {
+
+// A simulated system clock is a concrete virtual SystemClock implementation
+// that does not "tick" on its own. Time is advanced by explicit calls to
+// AdvanceTime() or SetTime() functions. This can be used as stub for testing
+// which can be dependency injected. Be careful when using SetTime() to not
+// violate the is_monotonic requirement, in other words avoid going backwards
+// unless initializing the clock before consumers have a reference to the clock.
+//
+// Example:
+// SimulatedSystemClock sim_system_clock;
+//
+// SystemClock::time_point now = sim_system_clock.now();
+// // now.time_since_epoch.duration() == std::chrono::seconds(0)
+//
+// sim_system_clock.AdvanceTime(std::chrono::seconds(42));
+// // now.time_since_epoch.duration() == std::chrono::seconds(42)
+//
+// This code is thread & IRQ safe.
+class SimulatedSystemClock : public VirtualSystemClock {
+ public:
+ SimulatedSystemClock(SystemClock::time_point timestamp =
+ SystemClock::time_point(SystemClock::duration(0)))
+ : current_timestamp_(timestamp) {}
+
+ void AdvanceTime(SystemClock::duration duration) {
+ std::lock_guard lock(spin_lock_);
+ current_timestamp_ += duration;
+ }
+
+ // WARNING: Use of this function may violate the is_monotonic clock attribute.
+ void SetTime(SystemClock::time_point timestamp) {
+ std::lock_guard lock(spin_lock_);
+ current_timestamp_ = timestamp;
+ }
+
+ SystemClock::time_point now() override {
+ std::lock_guard lock(spin_lock_);
+ return current_timestamp_;
+ };
+
+ private:
+ // In theory atomics could be used if 64bit atomics are supported, however
+ // performance of this test object shouldn't matter.
+ sync::SpinLock spin_lock_;
+ SystemClock::time_point current_timestamp_;
+};
+
+} // namespace pw::chrono
diff --git a/pw_chrono/public/pw_chrono/system_clock.h b/pw_chrono/public/pw_chrono/system_clock.h
new file mode 100644
index 0000000..82e5fbb
--- /dev/null
+++ b/pw_chrono/public/pw_chrono/system_clock.h
@@ -0,0 +1,171 @@
+// 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
+
+#include <stddef.h>
+#include <stdint.h>
+
+#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
+// 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"
+
+namespace pw::chrono {
+namespace backend {
+
+// The ARM AEBI does not permit the opaque 'time_point' to be passed via
+// registers, ergo the underlying fundamental type is forward declared.
+// A SystemCLock tick has the units of one SystemClock::period duration.
+// This must be thread and IRQ safe and provided by the backend.
+int64_t GetSystemClockTickCount();
+
+} // namespace backend
+
+// The SystemClock represents an unsteady, monotonic clock.
+//
+// The epoch of this clock is unspecified and may not be related to wall time
+// (for example, it can be time since boot). The time between ticks of this
+// clock may vary due to sleep modes and potential interrupt handling.
+// SystemClock meets the requirements of C++'s TrivialClock and Pigweed's
+// PigweedClock.
+//
+// Example:
+//
+// SystemClock::time_point before = SystemClock::now();
+// TakesALongTime();
+// SystemClock::duration time_taken = SystemClock::now() - before;
+// bool took_way_too_long = false;
+// if (time_taken > std::chrono::seconds(42)) {
+// took_way_too_long = true;
+// }
+//
+// This code is thread & IRQ safe, it may be NMI safe depending on is_nmi_safe.
+struct SystemClock {
+ using rep = int64_t;
+ // The period must be provided by the backend.
+ using period = backend::SystemClockPeriodSecondsRatio;
+ using duration = std::chrono::duration<rep, period>;
+ using time_point = std::chrono::time_point<SystemClock>;
+ // The epoch m ust be provided by the backend.
+ static constexpr Epoch epoch = backend::kSystemClockEpoch;
+
+ // The time points of this clock cannot decrease, however the time between
+ // ticks of this clock may slightly vary due to sleep modes. The duration
+ // during sleep may be ignored or backfilled with another clock.
+ static constexpr bool is_monotonic = true;
+ static constexpr bool is_steady = false;
+
+ // The now() function may not move forward while in a critical section or
+ // interrupt. This must be provided by the backend.
+ static constexpr bool is_free_running = backend::kSystemClockFreeRunning;
+
+ // The clock must stop while in halting debug mode.
+ static constexpr bool is_stopped_in_halting_debug_mode = true;
+
+ // The now() function can be invoked at any time.
+ static constexpr bool is_always_enabled = true;
+
+ // The now() function may work in non-masking interrupts, depending on the
+ // backend. This must be provided by the backend.
+ static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe;
+
+ // This is thread and IRQ safe. This must be provided by the backend.
+ static time_point now() {
+ return time_point(duration(backend::GetSystemClockTickCount()));
+ }
+};
+
+// An abstract interface representing a SystemClock.
+//
+// This interface allows decoupling code that uses time from the code that
+// creates a point in time. You can use this to your advantage by injecting
+// Clocks into interfaces rather than having implementations call
+// SystemClock::now() directly. However, this comes at a cost of a vtable per
+// implementation and more importantly passing and maintaining references to the
+// VirtualSystemCLock for all of the users.
+//
+// The VirtualSystemClock::RealClock() function returns a reference to the
+// real global SystemClock.
+//
+// Example:
+//
+// void DoFoo(VirtualSystemClock& system_clock) {
+// SystemClock::time_point now = clock.now();
+// // ... Code which consumes now.
+// }
+//
+// // Production code:
+// DoFoo(VirtualSystemCLock::RealClock);
+//
+// // Test code:
+// MockClock test_clock();
+// DoFoo(test_clock);
+//
+// This interface is thread and IRQ safe.
+class VirtualSystemClock {
+ public:
+ // Returns a reference to the real system clock to aid instantiation.
+ static VirtualSystemClock& RealClock();
+
+ virtual ~VirtualSystemClock() = default;
+ virtual SystemClock::time_point now() = 0;
+};
+
+} // namespace pw::chrono
+
+// The backend can opt to include an inlined implementation of the following:
+// int64_t GetSystemClockTickCount();
+#if __has_include("pw_chrono_backend/system_clock_inline.h")
+#include "pw_chrono_backend/system_clock_inline.h"
+#endif
+
+#endif // __cplusplus
+
+PW_EXTERN_C_START
+
+typedef int64_t pw_chrono_SystemClock_TickCount;
+typedef struct {
+ pw_chrono_SystemClock_TickCount ticks_since_epoch;
+} pw_chrono_SystemClock_TimePoint;
+typedef int64_t pw_chrono_SystemClock_Nanoseconds;
+
+// Returns the current time, see SystemClock::now() for more detail.
+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_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();
+
+// Warning, this may be lossy due to the use of std::chrono::duration_cast,
+// rounding towards zero.
+pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_TickCountToNsTruncate(
+ pw_chrono_SystemClock_TickCount ticks);
+
+PW_EXTERN_C_END
diff --git a/pw_chrono/simulated_system_clock_test.cc b/pw_chrono/simulated_system_clock_test.cc
new file mode 100644
index 0000000..5102a99
--- /dev/null
+++ b/pw_chrono/simulated_system_clock_test.cc
@@ -0,0 +1,54 @@
+// 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.
+
+#include "pw_chrono/simulated_system_clock.h"
+
+#include "gtest/gtest.h"
+
+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.
+constexpr auto kRoundedArbitraryDuration =
+ std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
+
+TEST(SimulatedSystemClock, InitialTime) {
+ SimulatedSystemClock clock;
+
+ EXPECT_EQ(SystemClock::time_point(SystemClock::duration(0)), clock.now());
+}
+
+TEST(SimulatedSystemClock, SetTime) {
+ SimulatedSystemClock clock;
+
+ clock.SetTime(pw::chrono::SystemClock::time_point(kRoundedArbitraryDuration));
+ EXPECT_EQ(kRoundedArbitraryDuration, clock.now().time_since_epoch());
+}
+
+TEST(SimulatedSystemClock, AdvanceTime) {
+ SimulatedSystemClock clock;
+
+ const SystemClock::time_point before_timestamp = clock.now();
+ clock.AdvanceTime(kRoundedArbitraryDuration);
+ const SystemClock::time_point after_timestamp = clock.now();
+
+ EXPECT_EQ(kRoundedArbitraryDuration, clock.now().time_since_epoch());
+ EXPECT_EQ(kRoundedArbitraryDuration, after_timestamp - before_timestamp);
+}
+
+} // namespace
+} // namespace pw::chrono
diff --git a/pw_chrono/system_clock.cc b/pw_chrono/system_clock.cc
new file mode 100644
index 0000000..cbf8d45
--- /dev/null
+++ b/pw_chrono/system_clock.cc
@@ -0,0 +1,59 @@
+// 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.
+
+#include "pw_chrono/system_clock.h"
+
+namespace pw::chrono {
+namespace {
+
+class RealSystemClock final : public VirtualSystemClock {
+ public:
+ SystemClock::time_point now() final { return SystemClock::now(); }
+};
+RealSystemClock real_system_clock;
+
+} // namespace
+
+VirtualSystemClock& VirtualSystemClock::RealClock() {
+ return real_system_clock;
+}
+
+} // 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()};
+}
+
+extern "C" pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_TimeDelta(
+ 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;
+}
+
+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))
+ .count();
+}
diff --git a/pw_chrono/system_clock_facade_test.cc b/pw_chrono/system_clock_facade_test.cc
new file mode 100644
index 0000000..1d60b11
--- /dev/null
+++ b/pw_chrono/system_clock_facade_test.cc
@@ -0,0 +1,122 @@
+// 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.
+
+#include <chrono>
+
+#include "gtest/gtest.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_preprocessor/util.h"
+
+namespace pw::chrono {
+namespace {
+
+extern "C" {
+
+// 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_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);
+
+} // extern "C"
+
+// While testing that the clock ticks (i.e. moves forward) we want to ensure a
+// failure can be reported instead of deadlocking the test until it passes.
+// Given that there isn't really a good heuristic for this we instead make some
+// wild assumptions to bound the maximum busy loop iterations.
+// - Assume our clock is < 6Ghz
+// - Assume we can check the clock in a single cycle
+// - Wait for up to 1/10th of a second @ 6Ghz, this may be a long period on a
+// slower (i.e. real) machine.
+constexpr uint64_t kMaxIterations = 6'000'000'000 / 10;
+
+TEST(SystemClock, Now) {
+ const SystemClock::time_point start_time = SystemClock::now();
+ // Verify the clock moves forward.
+ bool clock_moved_forward = false;
+ for (uint64_t i = 0; i < kMaxIterations; ++i) {
+ if (SystemClock::now() > start_time) {
+ clock_moved_forward = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(clock_moved_forward);
+}
+
+TEST(VirtualSystemClock, Now) {
+ auto& clock = VirtualSystemClock::RealClock();
+ const SystemClock::time_point start_time = clock.now();
+ // Verify the clock moves forward.
+ bool clock_moved_forward = false;
+ for (uint64_t i = 0; i < kMaxIterations; ++i) {
+ if (clock.now() > start_time) {
+ clock_moved_forward = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(clock_moved_forward);
+}
+
+TEST(SystemClock, NowInC) {
+ const pw_chrono_SystemClock_TimePoint start_time =
+ pw_chrono_SystemClock_CallNow();
+ // 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) {
+ clock_moved_forward = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(clock_moved_forward);
+}
+
+TEST(SystemClock, TimeDeltaInC) {
+ 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());
+}
+
+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.
+ 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()));
+}
+
+} // namespace
+} // namespace pw::chrono
diff --git a/pw_chrono/system_clock_facade_test_c.c b/pw_chrono/system_clock_facade_test_c.c
new file mode 100644
index 0000000..1ae3456
--- /dev/null
+++ b/pw_chrono/system_clock_facade_test_c.c
@@ -0,0 +1,42 @@
+// 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.
+
+// These tests call the pw_chrono module system_clock API from C. The return
+// values are checked in the main C++ tests.
+
+#include "pw_chrono/system_clock.h"
+
+pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_CallNow() {
+ return pw_chrono_SystemClock_Now();
+}
+
+pw_chrono_SystemClock_TickCount pw_chrono_SystemClock_CallTimeDelta(
+ pw_chrono_SystemClock_TimePoint last_time,
+ pw_chrono_SystemClock_TimePoint current_time) {
+ return pw_chrono_SystemClock_TimeDelta(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);
+}
diff --git a/pw_chrono_stl/BUILD b/pw_chrono_stl/BUILD
new file mode 100644
index 0000000..e1abacf
--- /dev/null
+++ b/pw_chrono_stl/BUILD
@@ -0,0 +1,47 @@
+# 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.
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_library",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache License 2.0
+
+pw_cc_library(
+ name = "system_clock_headers",
+ hdrs = [
+ "public/pw_chrono_stl/system_clock_config.h",
+ "public/pw_chrono_stl/system_clock_inline.h",
+ "public_overrides/pw_chrono_backend/system_clock_config.h",
+ "public_overrides/pw_chrono_backend/system_clock_inline.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides",
+ ],
+ deps = [
+ "//pw_chrono:epoch",
+ ],
+)
+
+pw_cc_library(
+ name = "system_clock",
+ deps = [
+ ":system_clock_headers",
+ "//pw_chrono:system_clock_facade",
+ ],
+)
diff --git a/pw_chrono_stl/BUILD.gn b/pw_chrono_stl/BUILD.gn
new file mode 100644
index 0000000..948738d
--- /dev/null
+++ b/pw_chrono_stl/BUILD.gn
@@ -0,0 +1,50 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+
+config("public_include_path") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+}
+
+config("backend_config") {
+ include_dirs = [ "public_overrides" ]
+ visibility = [ ":*" ]
+}
+
+# This target provides the backend for pw::chrono::SystemClock.
+pw_source_set("system_clock") {
+ public_configs = [
+ ":public_include_path",
+ ":backend_config",
+ ]
+ public = [
+ "public/pw_chrono_stl/system_clock_config.h",
+ "public/pw_chrono_stl/system_clock_inline.h",
+ "public_overrides/pw_chrono_backend/system_clock_config.h",
+ "public_overrides/pw_chrono_backend/system_clock_inline.h",
+ ]
+ public_deps = [
+ "$dir_pw_chrono:epoch",
+ "$dir_pw_chrono:system_clock.facade",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_chrono_stl/docs.rst b/pw_chrono_stl/docs.rst
new file mode 100644
index 0000000..8160deb
--- /dev/null
+++ b/pw_chrono_stl/docs.rst
@@ -0,0 +1,26 @@
+.. _module-pw_chrono_stl:
+
+-------------
+pw_chrono_stl
+-------------
+``pw_chrono_stl`` is a collection of ``pw_chrono`` backends that are implemented
+using STL's ``std::chrono`` library.
+
+.. warning::
+ This module is under construction, not ready for use, and the documentation
+ is incomplete.
+
+SystemClock backend
+-------------------
+The STL based ``system_clock`` backend implements the ``pw_chrono:system_clock``
+facade by using the ``std::chrono::steady_clock``. Note that the
+``std::chrono::system_clock`` cannot be used as this is not always a monotonic
+clock source.
+
+See the documentation for ``pw_chrono`` for further details.
+
+Build targets
+-------------
+The GN build for ``pw_chrono_stl`` has one target: ``system_clock``.
+The ``system_clock`` target provides the ``pw_chrono_stl/system_clock.h`` header
+and the backend for the ``pw_chrono:system_clock``.
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
new file mode 100644
index 0000000..05b1c71
--- /dev/null
+++ b/pw_chrono_stl/public/pw_chrono_stl/system_clock_config.h
@@ -0,0 +1,34 @@
+// 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
+
+#include <chrono>
+
+#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;
+
+// The std::chrono::steady_clock can be used by signal handlers.
+constexpr inline bool kSystemClockNmiSafe = true;
+
+// The std::chrono::steady_clock ticks while in a signal handler.
+constexpr inline bool kSystemClockFreeRunning = true;
+
+} // namespace pw::chrono::backend
diff --git a/pw_chrono_stl/public/pw_chrono_stl/system_clock_inline.h b/pw_chrono_stl/public/pw_chrono_stl/system_clock_inline.h
new file mode 100644
index 0000000..9fa0d5e
--- /dev/null
+++ b/pw_chrono_stl/public/pw_chrono_stl/system_clock_inline.h
@@ -0,0 +1,28 @@
+// 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
+
+#include <chrono>
+
+#include "pw_chrono/system_clock.h"
+
+namespace pw::chrono::backend {
+
+inline int64_t GetSystemClockTickCount() {
+ // Note that no conversion is necessary since the steady_clock's period and
+ // epoch are directly used.
+ return std::chrono::steady_clock::now().time_since_epoch().count();
+}
+
+} // namespace pw::chrono::backend
diff --git a/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_config.h b/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_config.h
new file mode 100644
index 0000000..91c7ed8
--- /dev/null
+++ b/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_config.h
@@ -0,0 +1,19 @@
+// 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.
+
+// This override header includes the main tokenized logging header and defines
+// the PW_LOG macro as the tokenized logging macro.
+#pragma once
+
+#include "pw_chrono_stl/system_clock_config.h"
diff --git a/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_inline.h b/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_inline.h
new file mode 100644
index 0000000..eac3f56
--- /dev/null
+++ b/pw_chrono_stl/public_overrides/pw_chrono_backend/system_clock_inline.h
@@ -0,0 +1,19 @@
+// 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.
+
+// This override header includes the main tokenized logging header and defines
+// the PW_LOG macro as the tokenized logging macro.
+#pragma once
+
+#include "pw_chrono_stl/system_clock_inline.h"
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index 73c61c0..c757915 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -41,6 +41,9 @@
# Tokenizer trace time.
pw_trace_tokenizer_time = "$dir_pw_trace_tokenized:host_trace_time"
+ # Configure backend for pw_chrono's system_clock facade.
+ pw_chrono_SYSTEM_CLOCK_BACKEND = "$dir_pw_chrono_stl:system_clock"
+
# Specify builtin GN variables.
current_os = host_os
current_cpu = host_cpu