[sw/silicon_creator] Add driver for retention SRAM

Add a simple driver that provides typed access to the retention SRAM
and a function to clear it.

This change puts in place a temporary memory map that will need to
be updated when #5760 is finalized.

Signed-off-by: Michael Munday <mike.munday@lowrisc.org>
diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build
index c81a33b..e7a36c5 100644
--- a/sw/device/silicon_creator/lib/drivers/meson.build
+++ b/sw/device/silicon_creator/lib/drivers/meson.build
@@ -318,6 +318,35 @@
   suite: 'mask_rom',
 )
 
+# Mask ROM retention SRAM driver
+sw_silicon_creator_lib_driver_retention_sram = declare_dependency(
+  link_with: static_library(
+    'sw_silicon_creator_lib_driver_retention_sram',
+    sources: [
+      hw_ip_sram_ctrl_reg_h,
+      'retention_sram.c',
+    ],
+    dependencies: [
+      sw_silicon_creator_lib_base_abs_mmio,
+    ],
+  ),
+)
+
+sw_silicon_creator_lib_driver_retention_sram_functest = declare_dependency(
+  link_with: static_library(
+    'sw_silicon_creator_lib_driver_retention_sram_functest',
+    sources: ['retention_sram_functest.c'],
+    dependencies: [
+      sw_silicon_creator_lib_driver_retention_sram,
+    ],
+  ),
+)
+mask_rom_tests += {
+  'sw_silicon_creator_lib_driver_retention_sram_functest': {
+    'library': sw_silicon_creator_lib_driver_retention_sram_functest,
+  }
+}
+
 # Mask ROM watchdog driver
 sw_silicon_creator_lib_driver_watchdog = declare_dependency(
   link_with: static_library(
@@ -393,6 +422,7 @@
       'watchdog_functest.c'
     ],
     dependencies: [
+      sw_silicon_creator_lib_driver_retention_sram,
       sw_silicon_creator_lib_driver_rstmgr,
       sw_silicon_creator_lib_driver_watchdog,
     ],
diff --git a/sw/device/silicon_creator/lib/drivers/retention_sram.c b/sw/device/silicon_creator/lib/drivers/retention_sram.c
new file mode 100644
index 0000000..56f2c0e
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/retention_sram.c
@@ -0,0 +1,19 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
+
+#include <assert.h>
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+volatile retention_sram_t *retention_sram_get(void) {
+  static_assert(sizeof(retention_sram_t) == TOP_EARLGREY_RAM_RET_AON_SIZE_BYTES,
+                "Unexpected retention SRAM size.");
+  return (volatile retention_sram_t *)TOP_EARLGREY_RAM_RET_AON_BASE_ADDR;
+}
+
+void retention_sram_clear(void) {
+  *retention_sram_get() = (retention_sram_t){0};
+}
diff --git a/sw/device/silicon_creator/lib/drivers/retention_sram.h b/sw/device/silicon_creator/lib/drivers/retention_sram.h
new file mode 100644
index 0000000..6744448
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/retention_sram.h
@@ -0,0 +1,84 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_RETENTION_SRAM_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_RETENTION_SRAM_H_
+
+#include "sw/device/lib/base/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The retention SRAM is memory that is used to retain information, such as a
+ * boot service request, across a device reset. If the reset reason is 'power
+ * on' (POR) all fields will be initialized to zero by the mask ROM.
+ *
+ * TODO(lowRISC/opentitan#5760): the memory map for the retention SRAM is not
+ * yet finalized. When it is the layout and content of this structure should
+ * be frozen.
+ */
+typedef struct retention_sram {
+  /**
+   * A boot services request.
+   *
+   * TODO(lowRISC/opentitan#5760): enumerate boot service identifiers.
+   */
+  uint32_t boot_info;
+
+  /**
+   * Space reserved for future allocation by the silicon creator.
+   *
+   * TODO(lowRISC/opentitan#5760): the size / offset of this allocation should
+   * be reviewed.
+   */
+  uint32_t reserved_creator[447];
+
+  /**
+   * Panic record.
+   *
+   * TODO(lowRISC/opentitan#5760): placeholder, this is for saving a detailed
+   * crashdump record when the CPU is able to respond to a fault or alert
+   * escalation. The size / offset of this allocation should be reviewed.
+   */
+  uint32_t panic_record[256 / sizeof(uint32_t)];
+
+  /**
+   * Space reserved for allocation by the silicon owner.
+   *
+   * The silcon creator boot stages will not modify this field except for
+   * clearing it at initial power on.
+   *
+   * Tests that need to trigger (or detect) a device reset may use this field to
+   * preserve state information across resets.
+   *
+   * TODO(lowRISC/opentitan#5760): the size / offset of this allocation should
+   * be reviewed.
+   */
+  uint32_t reserved_owner[2048 / sizeof(uint32_t)];
+} retention_sram_t;
+
+OT_ASSERT_MEMBER_OFFSET(retention_sram_t, boot_info, 0);
+OT_ASSERT_MEMBER_OFFSET(retention_sram_t, panic_record, 1792);
+OT_ASSERT_MEMBER_OFFSET(retention_sram_t, reserved_owner, 2048);
+OT_ASSERT_SIZE(retention_sram_t, 4096);
+
+/**
+ * Returns a typed pointer to the retention SRAM.
+ *
+ * @return A pointer to the retention SRAM.
+ */
+volatile retention_sram_t *retention_sram_get(void);
+
+/**
+ * Clear the retention SRAM by setting every word to 0.
+ */
+void retention_sram_clear(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_RETENTION_SRAM_H_
diff --git a/sw/device/silicon_creator/lib/drivers/retention_sram_functest.c b/sw/device/silicon_creator/lib/drivers/retention_sram_functest.c
new file mode 100644
index 0000000..ab92318
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/retention_sram_functest.c
@@ -0,0 +1,48 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/lib/test_main.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+static rom_error_t retention_sram_clear_test(void) {
+  // Set every bit in the retention SRAM to one.
+  // Note: memset cannot be used directly because it discards the volatile
+  // qualifier.
+  volatile retention_sram_t *ret = retention_sram_get();
+  retention_sram_t ones;
+  memset(&ones, 0xff, sizeof(retention_sram_t));
+  *ret = ones;
+
+  // Clear the retention SRAM (set every bit to zero).
+  retention_sram_clear();
+
+  // Check that the retention SRAM was fully cleared.
+  // Note: memcmp cannot be used directly because it discards the volatile
+  // qualifier.
+  retention_sram_t zeros;
+  memset(&zeros, 0, sizeof(retention_sram_t));
+  retention_sram_t result = *ret;
+  if (memcmp(&zeros, &result, sizeof(retention_sram_t)) != 0) {
+    LOG_ERROR("Retention SRAM not cleared.");
+    return kErrorUnknown;  // Unreachable.
+  }
+  return kErrorOk;
+}
+
+const test_config_t kTestConfig;
+
+bool test_main(void) {
+  rom_error_t result = kErrorOk;
+  EXECUTE_TEST(result, retention_sram_clear_test);
+  return result == kErrorOk;
+}
diff --git a/sw/device/silicon_creator/lib/drivers/watchdog_functest.c b/sw/device/silicon_creator/lib/drivers/watchdog_functest.c
index 1d478ff..972d3d7 100644
--- a/sw/device/silicon_creator/lib/drivers/watchdog_functest.c
+++ b/sw/device/silicon_creator/lib/drivers/watchdog_functest.c
@@ -13,6 +13,7 @@
 #include "sw/device/lib/runtime/print.h"
 #include "sw/device/lib/testing/check.h"
 #include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
 #include "sw/device/silicon_creator/lib/drivers/rstmgr.h"
 #include "sw/device/silicon_creator/lib/drivers/watchdog.h"
 #include "sw/device/silicon_creator/lib/error.h"
@@ -21,9 +22,6 @@
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 #include "rstmgr_regs.h"
 
-void *const kRetentionRamBase = (void *)TOP_EARLGREY_RAM_RET_AON_BASE_ADDR;
-const size_t kRetentionRamSize = TOP_EARLGREY_RAM_RET_AON_SIZE_BYTES;
-
 // Tests that we can pet the watchdog and avoid a reset.
 rom_error_t watchdog_pet_test(void) {
   watchdog_init(500);
@@ -68,10 +66,13 @@
   // This test assumes the reset reason is unique.
   CHECK(bitfield_popcount32(reason) == 1, "Expected exactly 1 reset reason.");
 
-  volatile test_phase_t *phase = (volatile test_phase_t *)kRetentionRamBase;
+  // Use the part of the retention SRAM reserved for the silicon owner to
+  // store the test phase.
+  volatile uint32_t *phase = &retention_sram_get()->reserved_owner[0];
+
   if (bitfield_bit32_read(reason, kRstmgrReasonPowerOn)) {
     // Power-on: zero out the retention RAM.
-    memset(kRetentionRamBase, 0, kRetentionRamSize);
+    retention_sram_clear();
 
     *phase = kTestPhasePet;
     EXECUTE_TEST(result, watchdog_pet_test);
diff --git a/sw/device/silicon_creator/lib/irq_asm_functest.c b/sw/device/silicon_creator/lib/irq_asm_functest.c
index d33036c..dd5f757 100644
--- a/sw/device/silicon_creator/lib/irq_asm_functest.c
+++ b/sw/device/silicon_creator/lib/irq_asm_functest.c
@@ -13,6 +13,7 @@
 #include "sw/device/lib/runtime/print.h"
 #include "sw/device/lib/testing/check.h"
 #include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
 #include "sw/device/silicon_creator/lib/drivers/rstmgr.h"
 #include "sw/device/silicon_creator/lib/drivers/watchdog.h"
 #include "sw/device/silicon_creator/lib/error.h"
@@ -20,9 +21,6 @@
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 
-void *const kRetentionRamBase = (void *)TOP_EARLGREY_RAM_RET_AON_BASE_ADDR;
-const size_t kRetentionRamSize = TOP_EARLGREY_RAM_RET_AON_SIZE_BYTES;
-
 /**
  * Exception handler written in assembly.
  *
@@ -51,10 +49,13 @@
   // This test assumes the reset reason is unique.
   CHECK(bitfield_popcount32(reason) == 1, "Expected exactly 1 reset reason.");
 
-  volatile test_phase_t *phase = (volatile test_phase_t *)kRetentionRamBase;
+  // Use the part of the retention SRAM reserved for the silicon owner to
+  // store the test phase.
+  volatile uint32_t *phase = &retention_sram_get()->reserved_owner[0];
+
   if (bitfield_bit32_read(reason, kRstmgrReasonPowerOn)) {
     // Power-on: zero out the retention RAM.
-    memset(kRetentionRamBase, 0, kRetentionRamSize);
+    retention_sram_clear();
 
     LOG_INFO("Calling exception handler to reset device.");
     *phase = kTestPhaseReset;
diff --git a/sw/device/silicon_creator/lib/meson.build b/sw/device/silicon_creator/lib/meson.build
index 62000c3..a1c55a3 100644
--- a/sw/device/silicon_creator/lib/meson.build
+++ b/sw/device/silicon_creator/lib/meson.build
@@ -236,6 +236,7 @@
     ],
     dependencies: [
       sw_silicon_creator_lib_irq_asm,
+      sw_silicon_creator_lib_driver_retention_sram,
       sw_silicon_creator_lib_driver_rstmgr,
       sw_silicon_creator_lib_driver_watchdog,
     ],
diff --git a/test/systemtest/earlgrey/config.py b/test/systemtest/earlgrey/config.py
index e8c626c..63f2c64 100644
--- a/test/systemtest/earlgrey/config.py
+++ b/test/systemtest/earlgrey/config.py
@@ -134,6 +134,10 @@
         "test_dir": "sw/device/silicon_creator/testing",
     },
     {
+        "name": "sw_silicon_creator_lib_driver_retention_sram_functest",
+        "test_dir": "sw/device/silicon_creator/testing",
+    },
+    {
         "name": "sw_silicon_creator_lib_driver_alert_functest",
         "test_dir": "sw/device/silicon_creator/testing",
         # TODO(lowRISC/opentitan#6965) This test resets the chip and appears to