pw_persistent_ram: Adds initial Persistent object

Adds the pw_persistent_ram module with initial documentation on
persistent RAM usage and life cycle management and the initial
Persistent<t> container with built in integrity checking.

Change-Id: I7f539228ba5fcc76c6d80a72110f2365724ebf23
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/38021
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: David Rogers <davidrogers@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/BUILD.gn b/BUILD.gn
index b650baf..c16c6ba 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -214,6 +214,7 @@
       "$dir_pw_hdlc",
       "$dir_pw_i2c",
       "$dir_pw_metric",
+      "$dir_pw_persistent_ram",
       "$dir_pw_polyfill",
       "$dir_pw_preprocessor",
       "$dir_pw_protobuf",
@@ -267,6 +268,7 @@
       "$dir_pw_malloc_freelist:tests",
       "$dir_pw_metric:tests",
       "$dir_pw_multisink:tests",
+      "$dir_pw_persistent_ram:tests",
       "$dir_pw_polyfill:tests",
       "$dir_pw_preprocessor:tests",
       "$dir_pw_protobuf:tests",
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index e904252..aa3c397 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -87,6 +87,7 @@
     "$dir_pw_module:docs",
     "$dir_pw_multisink:docs",
     "$dir_pw_package:docs",
+    "$dir_pw_persistent_ram:docs",
     "$dir_pw_polyfill:docs",
     "$dir_pw_preprocessor:docs",
     "$dir_pw_presubmit:docs",
diff --git a/modules.gni b/modules.gni
index 655f3fc..7da2072 100644
--- a/modules.gni
+++ b/modules.gni
@@ -63,6 +63,7 @@
   dir_pw_multisink = get_path_info("pw_multisink", "abspath")
   dir_pw_fuzzer = get_path_info("pw_fuzzer", "abspath")
   dir_pw_package = get_path_info("pw_package", "abspath")
+  dir_pw_persistent_ram = get_path_info("pw_persistent_ram", "abspath")
   dir_pw_polyfill = get_path_info("pw_polyfill", "abspath")
   dir_pw_preprocessor = get_path_info("pw_preprocessor", "abspath")
   dir_pw_presubmit = get_path_info("pw_presubmit", "abspath")
diff --git a/pw_persistent_ram/BUILD b/pw_persistent_ram/BUILD
new file mode 100644
index 0000000..3d35525
--- /dev/null
+++ b/pw_persistent_ram/BUILD
@@ -0,0 +1,42 @@
+# Copyright 2021 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
+
+pw_cc_library(
+    name = "pw_persistent_ram",
+    hdrs = [
+        "public/pw_persistent_ram/persistent.h",
+    ],
+    includes = ["public"],
+)
+
+pw_cc_test(
+    name = "persistent_test",
+    srcs = [
+        "persistent_test.cc",
+    ],
+    deps = [
+        ":pw_persistent_ram",
+        "//pw_unit_test",
+    ],
+)
diff --git a/pw_persistent_ram/BUILD.gn b/pw_persistent_ram/BUILD.gn
new file mode 100644
index 0000000..6c7bdcf
--- /dev/null
+++ b/pw_persistent_ram/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2021 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")
+import("$dir_pw_unit_test/test.gni")
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+pw_source_set("pw_persistent_ram") {
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_persistent_ram/persistent.h" ]
+  public_deps = [
+    dir_pw_assert,
+    dir_pw_checksum,
+  ]
+}
+
+pw_test_group("tests") {
+  tests = [ ":persistent_test" ]
+}
+
+pw_test("persistent_test") {
+  deps = [ ":pw_persistent_ram" ]
+  sources = [ "persistent_test.cc" ]
+}
+
+# TODO(ewout): add size reports in a follow up CL.
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_persistent_ram/docs.rst b/pw_persistent_ram/docs.rst
new file mode 100644
index 0000000..e0d94c6
--- /dev/null
+++ b/pw_persistent_ram/docs.rst
@@ -0,0 +1,167 @@
+.. _module-pw_persistent_ram:
+
+=================
+pw_persistent_ram
+=================
+The ``pw_persistent_ram`` module contains utilities and containers for using
+persistent RAM. By persistent RAM we are referring to memory which is not
+initialized across reboots by the hardware nor bootloader(s). This memory may
+decay or bit rot between reboots including brownouts, ergo integrity checking is
+highly recommended.
+
+.. Note::
+  This is something that not all architectures and applications built on them
+  support and requires hardware in the loop testing to verify it works as
+  intended.
+
+.. Warning::
+  Do not treat the current containers provided in this module as stable storage
+  primitives. We are still evaluating lighterweight checksums from a code size
+  point of view. In other words, future updates to this module may result in a
+  loss of persistent data across software updates.
+
+------------------------
+Persistent RAM Placement
+------------------------
+Persistent RAM is typically provided through specially carved out linker script
+sections and/or memory ranges which are located in such a way that any
+bootloaders and the application boot code do not clobber it.
+
+1. If persistent linker sections are provided, we recommend using our section
+   placement macro. For example imagine the persistent section name is called
+   `.noinit`, then you could instantiate an object as such:
+
+   .. code-block:: cpp
+
+      #include "pw_persistent_ram/persistent.h"
+      #include "pw_preprocessor/compiler.h"
+
+      using pw::persistent_ram::Persistent;
+
+      PW_KEEP_IN_SECTION(".noinit") Persistent<bool> persistent_bool;
+
+2. If persistent memory ranges are provided, we recommend using a struct to wrap
+   the different persisted objects. This then could be checked to fit in the
+   provided memory range size, for example by asserting against variables
+   provided through a linker script.
+
+   .. code-block:: cpp
+
+      #include "pw_assert/assert.h"
+      #include "pw_persistent_ram/persistent.h"
+
+      // Provided for example through a linker script.
+      extern "C" uint8_t __noinit_begin;
+      extern "C" uint8_t __noinit_end;
+
+      struct PersistentData {
+        Persistent<bool> persistent_bool;
+      };
+      PersistentData& persistent_data =
+        *reinterpret_cast<NoinitData*>(&__noinit_begin);
+
+      void CheckPersistentDataSize() {
+        PW_DCHECK_UINT_LE(sizeof(PersistentData),
+                          __noinit_end - __noinit_begin,
+                          "PersistentData overflowed the noinit memory range");
+      }
+
+-----------------------------------
+Persistent RAM Lifecycle Management
+-----------------------------------
+In order for persistent RAM containers to be as useful as possible, any
+invalidation of persistent RAM and the containers therein should be executed
+before the global static C++ constructors, but after the BSS and data sections
+are initialized in RAM.
+
+The preferred way to clear Persistent RAM is to simply zero entire persistent
+RAM sections and/or memory regions. Pigweed's persistents containers have picked
+integrity checks which work with zerod memory, meaning they do not hold a value
+after zeroing. Alternatively containers can be individually cleared.
+
+The boot sequence itself is tightly coupled to the number of persistent sections
+and/or memory regions which exist in the final image, ergo this is something
+which Pigweed cannot provide to the user directly. However, we do recommend
+following some guidelines:
+
+1. Do not instantiate regular types/objects in persistent RAM, ensure integrity
+   checking is always used! This is a major risk with this technique and can
+   lead to unexpected memory corruption.
+2. Always instantiate persistent containers outside of the objects which depend
+   on them and use dependency injection. This permits unit testing and avoids
+   placement accidents of persistents and/or their users.
+3. Always erase persistent RAM data after software updates unless the
+   persistent storage containers are explicitly stored at fixed address and
+   with a fixed layout. This prevents use of swapped objects or their members
+   where the same integrity checks are used.
+4. Consider zeroing persistent RAM to recover from crashes which may be induced
+   by persistent RAM usage, for example by checking the reboot/crash reason.
+5. Consider zeroing persistent RAM on cold boots to always start from a
+   consistent state if persistence is only desired across warm reboots. This can
+   create determinism from cold boots when using for example DRAM.
+6. Consider an explicit persistent clear request which can be set before a warm
+   reboot as a signal to zero all persistent RAM on the next boot to emulate
+   persistent memory loss in a threadsafe manner.
+
+---------------------------------
+pw::persistent_ram::Persistent<T>
+---------------------------------
+The Persistent is a simple container for holding its templated value ``T`` with
+CRC16 integrity checking. Note that a Persistent will be lost if a write/set
+operation is interrupted or otherwise not completed, as it is not double
+buffered.
+
+The default constructor does nothing, meaning it will result in either invalid
+state initially or a valid persisted value from a previous session.
+
+The destructor does nothing, ergo it is okay if it is not executed during
+shutdown.
+
+Example
+-------
+A common use case of persistent data is to track boot counts, or effectively
+how often the device has rebooted. This can be useful for monitoring how many
+times the device rebooted and/or crashed. This can be easily accomplished using
+the Persistent container.
+
+.. code-block:: cpp
+
+    #include "pw_persistent_ram/persistent.h"
+    #include "pw_preprocessor/compiler.h"
+
+    using pw::persistent_ram::Persistent;
+
+    class BootCount {
+     public:
+      explicit BootCount(Persistent<uint16_t>& persistent_boot_count)
+          : persistent_(persistent_boot_count) {
+        if (!persistent_.has_value()) {
+          persistent_ = 0;
+        } else {
+          persistent_ = persistent_.value() + 1;
+        }
+        boot_count_ = persistent_.value();
+      }
+
+      uint16_t GetBootCount() { return boot_count_; }
+
+     private:
+      Persistent<uint16_t>& persistent_;
+      uint16_t boot_count_;
+    };
+
+    PW_KEEP_IN_SECTION(".noinit") Persistent<uint16_t> persistent_boot_count;
+    BootCount boot_count(persistent_boot_count);
+
+    int main() {
+      const uint16_t boot_count = boot_count.GetBootCount();
+      // ... rest of main
+    }
+
+Compatibility
+-------------
+* C++17
+
+Dependencies
+------------
+* ``pw_checksum``
diff --git a/pw_persistent_ram/persistent_test.cc b/pw_persistent_ram/persistent_test.cc
new file mode 100644
index 0000000..6f45489
--- /dev/null
+++ b/pw_persistent_ram/persistent_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2021 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_persistent_ram/persistent.h"
+
+#include <type_traits>
+
+#include "gtest/gtest.h"
+
+namespace pw::persistent_ram {
+namespace {
+
+class PersistentTest : public ::testing::Test {
+ protected:
+  PersistentTest() { ZeroPersistentMemory(); }
+
+  // Emulate invalidation of persistent section(s).
+  void ZeroPersistentMemory() { memset(&buffer_, 0, sizeof(buffer_)); }
+
+  // Allocate a chunk of aligned storage that can be independently controlled.
+  std::aligned_storage_t<sizeof(Persistent<uint32_t>),
+                         alignof(Persistent<uint32_t>)>
+      buffer_;
+};
+
+TEST_F(PersistentTest, DefaultConstructionAndDestruction) {
+  {  // Emulate a boot where the persistent sections were invalidated.
+    // Although the fixture always does this, we do this an extra time to be
+    // 100% confident that an integrity check cannot be accidentally selected
+    // which results in reporting there is valid data when zero'd.
+    ZeroPersistentMemory();
+    auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
+    EXPECT_FALSE(persistent.has_value());
+
+    persistent = 42;
+    ASSERT_TRUE(persistent.has_value());
+    EXPECT_EQ(42u, persistent.value());
+
+    persistent.~Persistent();  // Emulate shutdown / global destructors.
+  }
+
+  {  // Emulate a boot where persistent memory was kept as is.
+    auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
+    ASSERT_TRUE(persistent.has_value());
+    EXPECT_EQ(42u, persistent.value());
+  }
+}
+
+TEST_F(PersistentTest, Reset) {
+  {  // Emulate a boot where the persistent sections were invalidated.
+    auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
+    persistent = 42u;
+    EXPECT_TRUE(persistent.has_value());
+    persistent.reset();
+
+    persistent.~Persistent();  // Emulate shutdown / global destructors.
+  }
+
+  {  // Emulate a boot where persistent memory was kept as is.
+    auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
+    EXPECT_FALSE(persistent.has_value());
+  }
+}
+
+TEST_F(PersistentTest, Emplace) {
+  auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
+  EXPECT_FALSE(persistent.has_value());
+
+  persistent.emplace(42u);
+  ASSERT_TRUE(persistent.has_value());
+  EXPECT_EQ(42u, persistent.value());
+}
+
+}  // namespace
+}  // namespace pw::persistent_ram
diff --git a/pw_persistent_ram/public/pw_persistent_ram/persistent.h b/pw_persistent_ram/public/pw_persistent_ram/persistent.h
new file mode 100644
index 0000000..06f356b
--- /dev/null
+++ b/pw_persistent_ram/public/pw_persistent_ram/persistent.h
@@ -0,0 +1,124 @@
+// Copyright 2021 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 <cstdint>
+#include <cstring>
+#include <span>
+#include <type_traits>
+#include <utility>
+
+#include "pw_assert/light.h"
+#include "pw_checksum/crc16_ccitt.h"
+
+namespace pw::persistent_ram {
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wuninitialized"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+
+// A simple container for holding a value T with CRC16 integrity checking.
+//
+// A Persistent is simply a value T plus integrity checking for use in a
+// persistent RAM section which is not initialized on boot.
+//
+// WARNING: Unlike a DoubleBufferedPersistent, a Persistent will be lost if a
+// write/set operation is interrupted or otherwise not completed.
+//
+// TODO(pwbug/348): Consider a different integrity check implementation which
+// does not use a 512B lookup table.
+template <typename T>
+class Persistent {
+ public:
+  // Constructor which does nothing, meaning it never sets the value.
+  constexpr Persistent() {}
+
+  Persistent(const Persistent&) = delete;  // Copy constructor is disabled.
+  Persistent(Persistent&&) = delete;       // Move constructor is disabled.
+  ~Persistent() {}                         // The destructor does nothing.
+
+  // Construct the value in-place.
+  template <class... Args>
+  const T& emplace(Args&&... args) {
+    new (const_cast<T*>(&contents_)) T(std::forward<Args>(args)...);
+    crc_ = Crc(contents_);
+    return const_cast<T&>(contents_);
+  }
+
+  // Assignment operator.
+  template <typename U = T>
+  Persistent& operator=(U&& value) {
+    contents_ = std::move(value);
+    crc_ = Crc(contents_);
+    return *this;
+  }
+
+  // Destroys any contained value.
+  void reset() {
+    // The trivial destructor is skipped as it's trivial.
+    std::memset(const_cast<T*>(&contents_), 0, sizeof(contents_));
+    crc_ = 0;
+  }
+
+  // Returns true if a value is held by the Persistent.
+  bool has_value() const {
+    return crc_ == Crc(contents_);  // There's a value if its CRC matches.
+  }
+
+  // Access the value.
+  //
+  // Precondition: has_value() must be true.
+  const T& value() const {
+    PW_ASSERT(has_value());
+    return const_cast<T&>(contents_);
+  }
+
+ private:
+  static_assert(std::is_trivially_copy_constructible<T>::value,
+                "If a Persistent persists across reboots, it is effectively "
+                "loaded through a trivial copy constructor.");
+  static_assert(std::is_trivially_destructible<T>::value,
+                "A Persistent's destructor does not invoke the value's "
+                "destructor, ergo only trivially destructible types are "
+                "supported.");
+
+  static uint16_t Crc(volatile const T& value) {
+    return checksum::Crc16Ccitt::Calculate(
+        std::as_bytes(std::span(const_cast<const T*>(&value), 1)));
+  }
+
+  // Use unions to denote that these members are never initialized by design and
+  // on purpose. Volatile is used to ensure that the compiler cannot optimize
+  // out operations where it seems like there is no further usage of a
+  // Persistent as this may be on the next boot.
+  union {
+    volatile T contents_;
+  };
+  union {
+    volatile uint16_t crc_;
+  };
+};
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+}  // namespace pw::persistent_ram