[test, systemtest] Add MaskROM + ROM_EXT apps selfchecking

This change allows to run both set of tests in CI (boot_rom and Silicon
Creator). At the moment only a single smoke test (UART) has been added
to run via Silicon Creator code.

Rationale:
The main idea is to add some intial MaskROM + ROM_EXT testing without
disturbing the existing test set-up.

The new added test has passed, checked locally:
```
cd $REPO_TOP
mkdir build-bin/hw

ln -sf \
`pwd`/build/lowrisc_systems_top_earlgrey_verilator_0.1/sim-verilator \
build-bin/hw/top_earlgrey

pytest test/systemtest/earlgrey/test_sim_verilator.py
```

Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/test/systemtest/earlgrey/test_sim_verilator.py b/test/systemtest/earlgrey/test_sim_verilator.py
index 4179acb..7063e3b 100644
--- a/test/systemtest/earlgrey/test_sim_verilator.py
+++ b/test/systemtest/earlgrey/test_sim_verilator.py
@@ -9,7 +9,7 @@
 
 import pytest
 
-from .. import config, utils
+from .. import config, silicon_creator_config, utils
 
 log = logging.getLogger(__name__)
 
@@ -17,8 +17,8 @@
 class VerilatorSimEarlgrey:
     UART0_SPEED = 7200  # see device/lib/arch/device_sim_verilator.c
 
-    def __init__(self, sim_path: Path, rom_elf_path: Path,
-                 otp_img_path: Path, work_dir: Path):
+    def __init__(self, sim_path: Path, rom_elf_path: Path, otp_img_path: Path,
+                 work_dir: Path):
         """ A verilator simulation of the Earl Grey toplevel """
         assert sim_path.is_file()
         self._sim_path = sim_path
@@ -47,8 +47,7 @@
         self.uart0_log_path = self._work_dir / 'uart0.log'
 
         cmd_sim = [
-            self._sim_path,
-            '--meminit=rom,' + str(self._rom_elf_path),
+            self._sim_path, '--meminit=rom,' + str(self._rom_elf_path),
             '--meminit=otp,' + str(self._otp_img_path),
             '+UARTDPI_LOG_uart0=' + str(self.uart0_log_path)
         ]
@@ -206,6 +205,42 @@
     return (bin_path, verilator_extra_args)
 
 
+@pytest.fixture(
+    params=silicon_creator_config.TEST_SILICON_CREATOR_APPS_SELFCHECKING,
+    ids=lambda param: param['name'])
+def app_silicon_creator_selfchecking(request, bin_dir):
+    """ A self-checking device application for Verilator simulation
+
+    Returns:
+        A set (elf_path, verilator_extra_args)
+    """
+
+    app_config = request.param
+
+    if 'name' not in app_config:
+        raise RuntimeError(
+            "Key 'name' not found in TEST_APPS_SILICON_CREATOR_SELFCHECKING")
+
+    if 'targets' in app_config and 'sim_verilator' not in app_config['targets']:
+        pytest.skip("Test %s skipped on Verilator." % app_config['name'])
+
+    if 'binary_name' in app_config:
+        binary_name = app_config['binary_name']
+    else:
+        binary_name = app_config['name']
+
+    if 'verilator_extra_args' in app_config:
+        verilator_extra_args = app_config['verilator_extra_args']
+    else:
+        verilator_extra_args = []
+
+    test_filename = binary_name + '_rom_ext_sim_verilator.elf'
+    bin_path = bin_dir / 'sw/device/tests' / test_filename
+    assert bin_path.is_file()
+
+    return (bin_path, verilator_extra_args)
+
+
 # The following tests use the UART output from the log file written by the
 # UARTDPI module, and not the simulated UART device (/dev/pty/N) to ensure
 # reliable testing: As soon as the device application finishes, the simulation
@@ -257,6 +292,31 @@
     sim.terminate()
 
 
+def test_apps_selfchecking_silicon_creator(tmp_path, bin_dir,
+                                           app_silicon_creator_selfchecking):
+    """
+    Run a self-checking application on a Earl Grey Verilator simulation
+
+    The ROM is initialized with the default boot ROM, the flash is initialized
+    with |app_selfchecking|.
+
+    Self-checking applications are expected to return PASS or FAIL in the end.
+    """
+
+    sim_path = bin_dir / "hw/top_earlgrey/Vtop_earlgrey_verilator"
+    rom_elf_path = bin_dir / "sw/device/mask_rom/mask_rom_sim_verilator.elf"
+    otp_img_path = bin_dir / "sw/device/otp_img/otp_img_sim_verilator.vmem"
+
+    sim = VerilatorSimEarlgrey(sim_path, rom_elf_path, otp_img_path, tmp_path)
+
+    sim.run(app_silicon_creator_selfchecking[0],
+            extra_sim_args=app_silicon_creator_selfchecking[1])
+
+    assert_selfchecking_test_passes(sim)
+
+    sim.terminate()
+
+
 @pytest.mark.skip(
     reason="Spiflash on Verilator isn't reliable currently. See issue #3708.")
 def test_spiflash(tmp_path, bin_dir):
diff --git a/test/systemtest/silicon_creator_config.py b/test/systemtest/silicon_creator_config.py
new file mode 100644
index 0000000..29b0554
--- /dev/null
+++ b/test/systemtest/silicon_creator_config.py
@@ -0,0 +1,24 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# List of self-checking test applications, which return PASS or FAIL after
+# completion.
+#
+# Each list entry is a dict with the following keys:
+#
+# name:
+#   Name of the test (required)
+# binary_name:
+#   Basename of the test binary. Default: name (optional)
+# verilator_extra_args:
+#   A list of additional command-line arguments passed to the Verilator
+#   simulation (optional).
+# targets:
+#   List of targets for which the test is executed. The test will be executed
+#   on all targets if not given (optional).
+TEST_SILICON_CREATOR_APPS_SELFCHECKING = [
+    {
+        "name": "dif_uart_smoketest",
+    },
+]