[sw] Implement test_coverage library

This change:
- Introduces the `test_coverage` library that provides LLVM profiling
  runtime support and a function for sending the LLVM profile buffer
  over UART, and
- Introduces a new meson option named `coverage` to define the relevant
  dependencies conditionally.

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/lib/testing/meson.build b/sw/device/lib/testing/meson.build
index d3df6b8..3cf9ea7 100644
--- a/sw/device/lib/testing/meson.build
+++ b/sw/device/lib/testing/meson.build
@@ -2,7 +2,7 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-# Test status libary.
+# Test status library.
 sw_lib_testing_test_status = declare_dependency(
   link_with: static_library(
     'test_status_ot',
@@ -15,6 +15,40 @@
   )
 )
 
+# NOP coverage dependencies when coverage is not enabled.
+sw_lib_testing_test_coverage = declare_dependency(
+  link_with: static_library(
+    'test_coverage_none',
+    sources: [files('test_coverage_none.c')],
+  ),
+)
+collect_coverage = declare_dependency()
+
+if coverage
+  # Test coverage library that provides runtime functions for LLVM profiling.
+  sw_lib_testing_test_coverage = declare_dependency(
+    link_with: static_library(
+      'test_coverage_llvm',
+      include_directories: sw_vendor_llvm_clang_rt_inc_dir,
+      sources: [
+        sw_vendor_llvm_clang_rt_sources,
+        files('test_coverage_llvm.c'),
+      ],
+      dependencies: [
+        sw_lib_mem,
+        sw_lib_uart,
+        sw_lib_base_log,
+      ],
+    ),
+  )
+
+  # Dependency for enabling coverage
+  collect_coverage = declare_dependency(
+    compile_args: ['-fprofile-instr-generate', '-fcoverage-mapping'],
+    dependencies: sw_lib_testing_test_coverage,
+  )
+endif
+
 sw_lib_testing_test_main = declare_dependency(
   link_with: static_library(
     'test_main_ot',
diff --git a/sw/device/lib/testing/test_coverage.h b/sw/device/lib/testing/test_coverage.h
new file mode 100644
index 0000000..4a0da29
--- /dev/null
+++ b/sw/device/lib/testing/test_coverage.h
@@ -0,0 +1,12 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * Sends the LLVM profile buffer along with its length and CRC32.
+ *
+ * This function must be called at the end of a test. Note that this profile
+ * data is raw and must be indexed before it can be used to generate coverage
+ * reports.
+ */
+void test_coverage_send_buffer(void);
diff --git a/sw/device/lib/testing/test_coverage_llvm.c b/sw/device/lib/testing/test_coverage_llvm.c
new file mode 100644
index 0000000..6328ca6
--- /dev/null
+++ b/sw/device/lib/testing/test_coverage_llvm.c
@@ -0,0 +1,76 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/testing/test_coverage.h"
+#include "sw/device/lib/base/log.h"
+#include "sw/device/lib/uart.h"
+#include "sw/vendor/llvm_clang_rt_profile/compiler-rt/lib/profile/InstrProfiling.h"
+#include <stdint.h>
+
+/**
+ * When the linker finds a definition of this symbol, it knows to skip loading
+ * the object which contains the profiling runtime’s static initializer. See
+ * https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#using-the-profiling-runtime-without-static-initializers
+ * for more information.
+ */
+int __llvm_profile_runtime;
+
+/**
+ * Computes the CRC32 of a buffer as expected by Python's `zlib.crc32()`. The
+ * implementation below is basically a simplified, i.e. byte-by-byte and without
+ * a lookup table, version of zlib's crc32. See
+ * https://github.com/madler/zlib/blob/2fa463bacfff79181df1a5270fb67cc679a53e71/crc32.c,
+ * lines 111-112 and 276-279.
+ */
+static uint32_t crc32(uint8_t *buf, size_t len) {
+  // CRC32 polynomial.
+  static const uint32_t kPoly = 0xEDB88320;
+  // Since we use a contiguous buffer, we don't need to call this function
+  // multiple times. That's why `crc` is not a parameter and is initialized to
+  // `UINT32_MAX`, i.e. `~0`.
+  uint32_t crc = UINT32_MAX;
+  for (size_t i = 0; i < len; ++i) {
+    crc ^= buf[i];
+    for (uint8_t j = 0; j < 8; ++j) {
+      bool lsb = crc & 1;
+      crc >>= 1;
+      if (lsb) {
+        crc ^= kPoly;
+      }
+    }
+  }
+  return ~crc;
+}
+
+/**
+ * Sends the given buffer as a hex string over UART.
+ */
+static void send_buffer(uint8_t *buf, size_t len) {
+  for (uint32_t i = 0; i < len; ++i) {
+    base_printf("%2X", buf[i]);
+  }
+}
+
+void test_coverage_send_buffer(void) {
+  // It looks like we don't have a way to read the profile buffer incrementally.
+  // Thus, we define the following buffer.
+  uint8_t buf[8192] = {0};
+  // Write the profile buffer to the buffer defined above.
+  uint32_t buf_size = (uint32_t)__llvm_profile_get_size_for_buffer();
+  if (buf_size > sizeof(buf)) {
+    LOG_ERROR("ERROR: LLVM profile buffer is too large: %u bytes.", buf_size);
+  } else {
+    __llvm_profile_write_buffer((char *)buf);
+    // Send the buffer along with its length and CRC32.
+    base_printf("\r\nLLVM profile data (length: %u, CRC32: ", buf_size);
+    uint32_t checksum = crc32(buf, buf_size);
+    send_buffer((uint8_t *)&checksum, sizeof(checksum));
+    base_printf("):\r\n");
+    send_buffer(buf, buf_size);
+    base_printf("\r\n");
+  }
+  // Send `EOT` so that `cat` can exit. Note that this requires enabling
+  // `icanon` using `stty`.
+  uart_send_char(4);
+}
diff --git a/sw/device/lib/testing/test_coverage_none.c b/sw/device/lib/testing/test_coverage_none.c
new file mode 100644
index 0000000..67af8f0
--- /dev/null
+++ b/sw/device/lib/testing/test_coverage_none.c
@@ -0,0 +1,9 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/testing/test_coverage.h"
+
+// This NOP function gets linked in when coverage is disabled. See
+// `test_coverage_llvm.c` for its actual definition when coverage is enabled.
+void test_coverage_send_buffer(void) {}