[util] Add templates to autogen ISR testutils
This adds two templates to autogenerate ISR testutil implementations (in
C) and a meson.build file to build the ISR testutil library.
Additionally, this commit updates the `util/autogen_testutils.py` script
to function with the new templates.
This partially addresses #9038.
Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/util/autogen_testutils.py b/util/autogen_testutils.py
index 5284a9d..4c25bdd 100755
--- a/util/autogen_testutils.py
+++ b/util/autogen_testutils.py
@@ -13,6 +13,7 @@
$ util/autogen_testutils.py
"""
+import argparse
import glob
import logging
import shutil
@@ -20,8 +21,10 @@
import sys
from pathlib import Path
+import hjson
from mako.template import Template
+import topgen.lib as topgen_lib
from autogen_banner import get_autogen_banner
from make_new_dif.ip import Ip
@@ -36,13 +39,36 @@
"ERROR: clang-format must be installed to format "
" autogenerated code to pass OpenTitan CI checks.")
+ # Parse command line args.
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--topcfg_path",
+ "-t",
+ type=Path,
+ default=(REPO_TOP / "hw/top_earlgrey/data/top_earlgrey.hjson"),
+ help="path of the top hjson file.",
+ )
+ args = parser.parse_args()
+
+ # Parse toplevel Hjson to get IPs that are templated / generated with IPgen.
+ try:
+ topcfg_text = args.topcfg_path.read_text()
+ except FileNotFoundError:
+ logging.error(f"hjson {args.topcfg_path} could not be found.")
+ sys.exit(1)
+ topcfg = hjson.loads(topcfg_text, use_decimal=True)
+ templated_modules = topgen_lib.get_templated_modules(topcfg)
+ ipgen_modules = topgen_lib.get_ipgen_modules(topcfg)
+
# Define input/output directories.
autogen_dif_directory = REPO_TOP / "sw/device/lib/dif/autogen"
testutils_templates_dir = REPO_TOP / "util/autogen_testutils"
autogen_testutils_dir = REPO_TOP / "sw/device/lib/testing/autogen"
# Create list of IPs to generate shared testutils code for. This is all IPs
- # that have a DIF library, that the testutils functions can use.
+ # that have a DIF library, that the testutils functions can use. Note, the
+ # templates will take care of only generating ISR testutil functions for IPs
+ # that can actually generate interrupts.
ips_with_difs = []
for autogen_dif_filename in glob.iglob(str(autogen_dif_directory / "*.h")):
# NOTE: the line below takes as input a file path
@@ -51,14 +77,18 @@
ip_name_snake = Path(autogen_dif_filename).stem[4:-8]
# NOTE: ip.name_long_* not needed for auto-generated files which
# are the only files (re-)generated in batch mode.
- ips_with_difs.append(Ip(ip_name_snake, "AUTOGEN"))
+ ips_with_difs.append(
+ Ip(ip_name_snake, "AUTOGEN", templated_modules, ipgen_modules))
ips_with_difs.sort(key=lambda ip: ip.name_snake)
# Create output directories if needed.
autogen_testutils_dir.mkdir(exist_ok=True)
# Auto-generate testutils files.
- for suffix in [".h", ".c"]:
+ for suffix in [".h", ".c", ".build"]:
+ comment_syntax = "#" if suffix == ".build" else "//"
+ if suffix == ".build":
+ comment_syntax = "#"
for testutils_template_path_str in glob.iglob(
str(testutils_templates_dir / f"*{suffix}.tpl")):
testutils_template_path = Path(testutils_template_path_str)
@@ -71,15 +101,17 @@
testutils_template.render(ips_with_difs=ips_with_difs,
autogen_banner=get_autogen_banner(
"util/autogen_testutils.py",
- comment="//")))
+ comment=comment_syntax)))
# Format autogenerated file with clang-format.
- try:
- subprocess.check_call(["clang-format", "-i", testutils])
- except subprocess.CalledProcessError:
- logging.error(
- f"failed to format {testutils} with clang-format.")
- sys.exit(1)
+ # Note: do not format meson build file.
+ if testutils.suffix != ".build":
+ try:
+ subprocess.check_call(["clang-format", "-i", testutils])
+ except subprocess.CalledProcessError:
+ logging.error(
+ f"failed to format {testutils} with clang-format.")
+ sys.exit(1)
print("testutils \"{}\" successfully written to {}.".format(
suffix, str(testutils)))
diff --git a/util/autogen_testutils/isr_testutils.c.tpl b/util/autogen_testutils/isr_testutils.c.tpl
new file mode 100644
index 0000000..b613760
--- /dev/null
+++ b/util/autogen_testutils/isr_testutils.c.tpl
@@ -0,0 +1,71 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+${autogen_banner}
+
+#include "sw/device/lib/testing/autogen/isr_testutils.h"
+
+#include "sw/device/lib/dif/dif_rv_plic.h"
+% for ip in ips_with_difs:
+ % if ip.irqs:
+ #include "sw/device/lib/dif/dif_${ip.name_snake}.h"
+ % endif
+% endfor
+#include "sw/device/lib/testing/check.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
+
+% for ip in ips_with_difs:
+ % if ip.irqs:
+ void isr_testutils_${ip.name_snake}_isr(
+ plic_isr_ctx_t plic_ctx,
+ ${ip.name_snake}_isr_ctx ${ip.name_snake}_ctx,
+ top_earlgrey_plic_peripheral_t *peripheral_serviced,
+ dif_${ip.name_snake}_irq_t *irq_serviced) {
+
+ // Claim the IRQ at the PLIC.
+ dif_rv_plic_irq_id_t plic_irq_id;
+ CHECK_DIF_OK(dif_rv_plic_irq_claim(
+ plic_ctx.rv_plic,
+ plic_ctx.hart_id,
+ &plic_irq_id));
+
+ // Get the peripheral the IRQ belongs to.
+ *peripheral_serviced = (top_earlgrey_plic_peripheral_t)
+ top_earlgrey_plic_interrupt_for_peripheral[plic_irq_id];
+
+ // Get the IRQ that was fired from the PLIC IRQ ID.
+ dif_${ip.name_snake}_irq_t irq = (dif_${ip.name_snake}_irq_t)(plic_irq_id -
+ ${ip.name_snake}_ctx.plic_${ip.name_snake}_start_irq_id);
+ *irq_serviced = irq;
+
+ // Check if it is supposed to be the only IRQ fired.
+ if (${ip.name_snake}_ctx.is_only_irq) {
+ dif_${ip.name_snake}_irq_state_snapshot_t snapshot;
+ CHECK_DIF_OK(dif_${ip.name_snake}_irq_get_state(
+ ${ip.name_snake}_ctx.${ip.name_snake},
+ % if ip.name_snake == "rv_timer":
+ plic_ctx.hart_id,
+ % endif
+ &snapshot));
+ CHECK(snapshot == (dif_${ip.name_snake}_irq_state_snapshot_t)(1 << irq),
+ "Only ${ip.name_snake} IRQ %d expected to fire. Actual IRQ state = %x",
+ irq, snapshot);
+ }
+
+ // Acknowledge the IRQ at the peripheral.
+ CHECK_DIF_OK(dif_${ip.name_snake}_irq_acknowledge(
+ ${ip.name_snake}_ctx.${ip.name_snake},
+ irq));
+
+ // Complete the IRQ at the PLIC.
+ CHECK_DIF_OK(dif_rv_plic_irq_complete(
+ plic_ctx.rv_plic,
+ plic_ctx.hart_id,
+ plic_irq_id));
+ }
+
+ % endif
+% endfor
+
diff --git a/util/autogen_testutils/isr_testutils.h.tpl b/util/autogen_testutils/isr_testutils.h.tpl
index 9e54895..1576e9a 100644
--- a/util/autogen_testutils/isr_testutils.h.tpl
+++ b/util/autogen_testutils/isr_testutils.h.tpl
@@ -7,23 +7,71 @@
#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_AUTOGEN_ISR_TESTUTILS_H_
#define OPENTITAN_SW_DEVICE_LIB_TESTING_AUTOGEN_ISR_TESTUTILS_H_
+#include "sw/device/lib/dif/dif_rv_plic.h"
% for ip in ips_with_difs:
% if ip.irqs:
- /*
- * Services an ${ip.name_snake} IRQ at the IP that raised it, and verifies this
- * matches the IRQ that was raised at the PLIC.
- *
- * @param ${ip.name_snake} A(n) ${ip.name_snake} DIF handle.
- * @param plic_irq_id The triggered PLIC IRQ ID.
- * @param plic_${ip.name_snake}_start_irq_id The PLIC IRQ ID where ${ip.name_snake} starts.
- * @param expected_${ip.name_snake}_irq The expected ${ip.name_snake} IRQ.
- * @param is_only_irq This is the only IRQ expected to be raised.
+ #include "sw/device/lib/dif/dif_${ip.name_snake}.h"
+ % endif
+% endfor
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
+
+/**
+ * A handle to a PLIC ISR context struct.
+ */
+typedef struct plic_isr_ctx {
+ /**
+ * A handle to a rv_plic.
*/
- void isr_testutils_${ip.name_snake}(dif_${ip.name_snake}_t *${ip.name_snake},
- dif_rv_plic_irq_id_t plic_irq_id,
- dif_rv_plic_irq_id_t plic_${ip.name_snake}_start_irq_id,
- dif_${ip.name_snake}_irq_id_t expected_${ip.name_snake}_irq,
- bool is_only_irq);
+ dif_rv_plic_t *rv_plic;
+ /**
+ * The HART ID associated with the PLIC (correspond to a PLIC "target").
+ */
+ uint32_t hart_id;
+} plic_isr_ctx_t;
+
+% for ip in ips_with_difs:
+ % if ip.irqs:
+ /**
+ * A handle to a ${ip.name_snake} ISR context struct.
+ */
+ typedef struct ${ip.name_snake}_isr_ctx {
+ /**
+ * A handle to a ${ip.name_snake}.
+ */
+ dif_${ip.name_snake}_t *${ip.name_snake};
+ /**
+ * The PLIC IRQ ID where this ${ip.name_snake} instance's IRQs start.
+ */
+ dif_rv_plic_irq_id_t plic_${ip.name_snake}_start_irq_id;
+ /**
+ * The ${ip.name_snake} IRQ that is expected to be encountered in the ISR.
+ */
+ dif_${ip.name_snake}_irq_t expected_irq;
+ /**
+ * Whether or not a single IRQ is expected to be encountered in the ISR.
+ */
+ bool is_only_irq;
+ } ${ip.name_snake}_isr_ctx;
+
+ % endif
+% endfor
+
+% for ip in ips_with_difs:
+ % if ip.irqs:
+ /**
+ * Services an ${ip.name_snake} IRQ.
+ *
+ * @param plic_ctx A PLIC ISR context handle.
+ * @param ${ip.name_snake}_ctx A(n) ${ip.name_snake} ISR context handle.
+ * @param[out] peripheral_serviced Out param for the peripheral that was serviced.
+ * @param[out] irq_serviced Out param for the IRQ that was serviced.
+ */
+ void isr_testutils_${ip.name_snake}_isr(
+ plic_isr_ctx_t plic_ctx,
+ ${ip.name_snake}_isr_ctx ${ip.name_snake}_ctx,
+ top_earlgrey_plic_peripheral_t *peripheral_serviced,
+ dif_${ip.name_snake}_irq_t *irq_serviced);
% endif
% endfor
diff --git a/util/autogen_testutils/meson.build.tpl b/util/autogen_testutils/meson.build.tpl
new file mode 100644
index 0000000..5f19009
--- /dev/null
+++ b/util/autogen_testutils/meson.build.tpl
@@ -0,0 +1,20 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+${autogen_banner}
+
+# ISR test utilities
+sw_lib_testing_isr_testutils = declare_dependency(
+ link_with: static_library(
+ 'sw_lib_testing_isr_testutils',
+ sources: ['isr_testutils.c'],
+ dependencies: [
+% for ip in ips_with_difs:
+ % if ip.irqs:
+ sw_lib_dif_${ip.name_snake},
+ % endif
+% endfor
+ ],
+ ),
+)