[test] Add example program for executing code from SRAM

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/examples/sram_program/BUILD b/sw/device/examples/sram_program/BUILD
new file mode 100644
index 0000000..33817bd
--- /dev/null
+++ b/sw/device/examples/sram_program/BUILD
@@ -0,0 +1,36 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+load("//rules:opentitan.bzl", "opentitan_ram_binary")
+load("//rules:linker.bzl", "ld_library")
+
+package(default_visibility = ["//visibility:public"])
+
+ld_library(
+    name = "sram_program_linker_script",
+    script = "sram_program.ld",
+    deps = [
+        "//hw/top_earlgrey/sw/autogen:top_earlgrey_memory",
+        "//sw/device:info_sections",
+    ],
+)
+
+opentitan_ram_binary(
+    name = "sram_program",
+    srcs = [
+        "sram_program.c",
+    ],
+    hdrs = [
+        "sram_program.h",
+    ],
+    archive_symbol_prefix = "sram_program",
+    deps = [
+        ":sram_program_linker_script",
+        "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+        "//sw/device/lib/base:macros",
+        "//sw/device/lib/dif:sram_ctrl",
+        "//sw/device/lib/runtime:log",
+        "//sw/device/lib/testing/test_framework:check",
+    ],
+)
diff --git a/sw/device/examples/sram_program/sram_program.c b/sw/device/examples/sram_program/sram_program.c
new file mode 100644
index 0000000..248682c
--- /dev/null
+++ b/sw/device/examples/sram_program/sram_program.c
@@ -0,0 +1,42 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/dif/dif_uart.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/testing/test_framework/check.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+static dif_uart_t uart0;
+
+enum {
+  kSramStart = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR,
+  kSramEnd = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR +
+             TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_SIZE_BYTES,
+};
+
+void sram_main() {
+  // Initialize UART.
+  CHECK_DIF_OK(dif_uart_init(
+      mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
+  CHECK_DIF_OK(
+      dif_uart_configure(&uart0, (dif_uart_config_t){
+                                     .baudrate = kUartBaudrate,
+                                     .clk_freq_hz = kClockFreqPeripheralHz,
+                                     .parity_enable = kDifToggleDisabled,
+                                     .parity = kDifUartParityEven,
+                                 }));
+  base_uart_stdout(&uart0);
+
+  // Read the program counter and check that we are executing from SRAM.
+  uint32_t pc = 0;
+  asm("auipc %[pc], 0;" : [pc] "=r"(pc));
+  LOG_INFO("PC: %p, SRAM: [%p, %p)", pc, kSramStart, kSramEnd);
+  CHECK(pc >= kSramStart && pc < kSramEnd, "PC is outside the main SRAM");
+}
diff --git a/sw/device/examples/sram_program/sram_program.ld b/sw/device/examples/sram_program/sram_program.ld
new file mode 100644
index 0000000..04abbff
--- /dev/null
+++ b/sw/device/examples/sram_program/sram_program.ld
@@ -0,0 +1,83 @@
+/* Copyright lowRISC contributors. */
+/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/**
+ * Linker script for an OpenTitan SRAM program.
+ *
+ * Portions of this file are Ibex-specific.
+ */
+
+OUTPUT_ARCH(riscv);
+
+/**
+ * Indicate that there are no dynamic libraries, whatsoever.
+ */
+__DYNAMIC = 0;
+
+INCLUDE hw/top_earlgrey/sw/autogen/top_earlgrey_memory.ld
+
+_stack_end = ORIGIN(ram_main) + LENGTH(ram_main);
+
+/**
+ * DV Log offset.
+ *
+ * Note: This definition is required by the `sw/device/info_sections.ld` script.
+ */
+_dv_log_offset = 0x10000;
+
+ENTRY(sram_main);
+
+SECTIONS {
+  /**
+   * SRAM programs consist only of a `.data` section that contains all input
+   * `.rodata`, `.data`, `.text`, and `.bss` sections.
+   */
+  .data : ALIGN(4){
+    /* This will get loaded into `gp`, and the linker will use that register for
+     * accessing data within [-2048,2047] of `__global_pointer$`.
+     *
+     * This is much cheaper (for small data) than materializing the
+     * address and loading from that (which will take one extra instruction).
+     *
+     * This must match the value used for functional tests to be able to call
+     * SRAM programs from functional tests. */
+    __global_pointer$ = . + 2048;
+
+    /* Place the entry point of the SRAM program to the start of the main SRAM
+     * so that we don't have to maintain a separate offset from the start of
+     * the RAM to the entry point. */
+    ASSERT(DEFINED(sram_main), "SRAM programs must define `sram_main()`");
+    KEEP(*(.text.sram_main))
+    . = ALIGN(4);
+
+    /* Place all input `.rodata`, `.data`, `.text`, and `.bss` sections in the
+     * main SRAM.
+     *
+     * Small data should come before larger data. This helps to ensure small
+     * globals are within 2048 bytes of the value of `gp`, making their accesses
+     * hopefully only take one instruction. Other data will likely need
+     * multiple instructions to load, so we're less concerned about address
+     * materialisation taking more than one instruction. */
+    KEEP(*(.srodata))
+    KEEP(*(.srodata.*))
+    KEEP(*(.rodata))
+    KEEP(*(.rodata.*))
+    . = ALIGN(4);
+    KEEP(*(.sdata))
+    KEEP(*(.sdata.*))
+    KEEP(*(.data))
+    KEEP(*(.data.*))
+    . = ALIGN(4);
+    KEEP(*(.text))
+    KEEP(*(.text.*))
+    . = ALIGN(4);
+    KEEP(*(.sbss))
+    KEEP(*(.sbss.*))
+    KEEP(*(.bss))
+    KEEP(*(.bss.*))
+    . = ALIGN(4);
+  } > ram_main
+
+  INCLUDE sw/device/info_sections.ld
+}