[bazel] Build and run RISC-V compliance tests with Bazel

Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/third_party/riscv-compliance/0001-Add-configurable-trap-alignment-and-entry-point-to-p.patch b/third_party/riscv-compliance/0001-Add-configurable-trap-alignment-and-entry-point-to-p.patch
new file mode 100644
index 0000000..f454667
--- /dev/null
+++ b/third_party/riscv-compliance/0001-Add-configurable-trap-alignment-and-entry-point-to-p.patch
@@ -0,0 +1,91 @@
+From e8b82ff045fca264917a7c5539bfcbe8ed22b7a0 Mon Sep 17 00:00:00 2001
+From: Greg Chadwick <gac@lowrisc.org>
+Date: Wed, 15 Apr 2020 15:45:31 +0100
+Subject: [PATCH 1/5] Add configurable trap alignment and entry point to p
+ test-env
+
+
+diff --git a/riscv-test-env/p/riscv_test.h b/riscv-test-env/p/riscv_test.h
+index eaa6758..9423523 100644
+--- a/riscv-test-env/p/riscv_test.h
++++ b/riscv-test-env/p/riscv_test.h
+@@ -60,14 +60,14 @@
+   csrw pmpaddr0, t0;                                                    \
+   li t0, PMP_NAPOT | PMP_R | PMP_W | PMP_X;                             \
+   csrw pmpcfg0, t0;                                                     \
+-  .align 2;                                                             \
++  .align TRAPALIGN;                                                     \
+ 1:
+ 
+ #define INIT_SPTBR                                                      \
+   la t0, 1f;                                                            \
+   csrw mtvec, t0;                                                       \
+   csrwi sptbr, 0;                                                       \
+-  .align 2;                                                             \
++  .align TRAPALIGN;                                                     \
+ 1:
+ 
+ #define DELEGATE_NO_TRAPS                                               \
+@@ -76,7 +76,7 @@
+   csrwi medeleg, 0;                                                     \
+   csrwi mideleg, 0;                                                     \
+   csrwi mie, 0;                                                         \
+-  .align 2;                                                             \
++  .align TRAPALIGN;                                                     \
+ 1:
+ 
+ #define RVTEST_ENABLE_SUPERVISOR                                        \
+@@ -125,6 +125,14 @@
+ #if defined(TRAPHANDLER)
+ #include TRAPHANDLER
+ #endif
++#if !defined(TRAPALIGN)
++#define TRAPALIGN 2
++#endif
++
++#if !defined(RVTEST_ENTRY)
++#define RVTEST_ENTRY _start
++#endif
++
+ 
+ #define INTERRUPT_HANDLER j other_exception /* No interrupts should occur */
+ 
+@@ -133,11 +141,11 @@
+         .align  6;                                                      \
+         .weak stvec_handler;                                            \
+         .weak mtvec_handler;                                            \
+-        .globl _start;                                                  \
+-_start:                                                                 \
++        .globl RVTEST_ENTRY;                                            \
++RVTEST_ENTRY:                                                           \
+         /* reset vector */                                              \
+         j reset_vector;                                                 \
+-        .align 2;                                                       \
++        .align TRAPALIGN;                                               \
+ trap_vector:                                                            \
+         /* test whether the test came from pass/fail */                 \
+         csrr t5, mcause;                                                \
+diff --git a/riscv-test-suite/rv32i/src/I-EBREAK-01.S b/riscv-test-suite/rv32i/src/I-EBREAK-01.S
+index 958eebc..32c074c 100644
+--- a/riscv-test-suite/rv32i/src/I-EBREAK-01.S
++++ b/riscv-test-suite/rv32i/src/I-EBREAK-01.S
+@@ -71,6 +71,7 @@ RV_COMPLIANCE_CODE_BEGIN
+ 
+     # ---------------------------------------------------------------------------------------------
+     # Exception handler
++.align TRAPALIGN
+ _trap_handler:
+     # increment return address
+     csrr    x30, mepc
+diff --git a/riscv-test-suite/rv32i/src/I-ECALL-01.S b/riscv-test-suite/rv32i/src/I-ECALL-01.S
+index 5278207..0bdee2a 100644
+--- a/riscv-test-suite/rv32i/src/I-ECALL-01.S
++++ b/riscv-test-suite/rv32i/src/I-ECALL-01.S
+@@ -73,6 +73,7 @@ RV_COMPLIANCE_CODE_BEGIN
+ 
+     # ---------------------------------------------------------------------------------------------
+     # Exception handler
++.align TRAPALIGN
+ _trap_handler:
+     # increment return address
+     csrr    x30, mepc
diff --git a/third_party/riscv-compliance/BUILD b/third_party/riscv-compliance/BUILD
new file mode 100644
index 0000000..c4061cd
--- /dev/null
+++ b/third_party/riscv-compliance/BUILD
@@ -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
+
+load(":defs.bzl", "TESTS", "rv_compliance_test")
+
+package(default_visibility = ["//visibility:public"])
+
+[[rv_compliance_test(test, arch) for test in tests] for arch, tests in TESTS.items()]
diff --git a/third_party/riscv-compliance/BUILD.riscv-compliance.bazel b/third_party/riscv-compliance/BUILD.riscv-compliance.bazel
new file mode 100644
index 0000000..510f055
--- /dev/null
+++ b/third_party/riscv-compliance/BUILD.riscv-compliance.bazel
@@ -0,0 +1,98 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+package(default_visibility = ["//visibility:public"])
+
+exports_files(glob(["**/*"]))
+
+# NOTE: The files below are included as genrules rather than as patches so that
+# they can be easilly edited without having to rebuild the patchset.
+genrule(
+    name = "compliance_test_hdr",
+    outs = ["compliance_test.h"],
+    cmd = """echo '\
+// RISC-V Compliance Test Header File
+
+#ifndef _COMPLIANCE_TEST_H
+#define _COMPLIANCE_TEST_H
+
+#include "riscv_test.h"
+
+//-----------------------------------------------------------------------
+// RV Compliance Macros
+//-----------------------------------------------------------------------
+#define RV_COMPLIANCE_HALT                                                    \
+        j end_rvc_test;                                                       \
+
+#define RV_COMPLIANCE_RV32M                                                   \
+        RVTEST_RV32M                                                          \
+
+
+#define RV_COMPLIANCE_CODE_BEGIN                                              \
+        RVTEST_CODE_BEGIN                                                     \
+
+#define RV_COMPLIANCE_CODE_END                                                \
+        RVTEST_CODE_END                                                       \
+
+#define RV_COMPLIANCE_DATA_BEGIN                                              \
+        .section .data;                                                       \
+        RVTEST_DATA_BEGIN                                                     \
+
+#define RV_COMPLIANCE_DATA_END                                                \
+        RVTEST_DATA_END                                                       \
+
+#endif // _COMPLIANCE_TEST_H
+' > $@
+    """,
+    visibility = ["//visibility:private"],
+)
+
+genrule(
+    name = "compliance_io_hdr",
+    outs = ["compliance_io.h"],
+    cmd = """echo '\
+// RISC-V Compliance IO Test Header File
+
+#ifndef _COMPLIANCE_IO_H
+#define _COMPLIANCE_IO_H
+
+//-----------------------------------------------------------------------
+// RV IO Macros (Non functional)
+//-----------------------------------------------------------------------
+
+#define RVTEST_IO_INIT
+#define RVTEST_IO_WRITE_STR(_SP, _STR)
+#define RVTEST_IO_CHECK()
+#define RVTEST_IO_ASSERT_GPR_EQ(_SP, _R, _I)
+#define RVTEST_IO_ASSERT_SFPR_EQ(_F, _R, _I)
+#define RVTEST_IO_ASSERT_DFPR_EQ(_D, _R, _I)
+
+#endif // _COMPLIANCE_IO_H
+' > $@
+    """,
+    visibility = ["//visibility:private"],
+)
+
+cc_library(
+    name = "riscv-test-env",
+    hdrs = [
+        "compliance_io.h",
+        "compliance_test.h",
+        "riscv-test-env/aw_test_macros.h",
+        "riscv-test-env/encoding.h",
+        "riscv-test-env/p/riscv_test.h",
+        "riscv-test-env/riscv_test_macros.h",
+        "riscv-test-env/test_macros.h",
+    ],
+    defines = [
+        "PRIV_MISA_S=0",
+        "PRIV_MISA_U=0",
+        "RVTEST_ENTRY=_rvc_start",
+        "TRAPALIGN=8",
+    ],
+    includes = [
+        "riscv-test-env",
+        "riscv-test-env/p",
+    ],
+)
diff --git a/third_party/riscv-compliance/compliance_main.S b/third_party/riscv-compliance/compliance_main.S
new file mode 100644
index 0000000..a39bf08
--- /dev/null
+++ b/third_party/riscv-compliance/compliance_main.S
@@ -0,0 +1,94 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+  .section .bss
+arch_state_store:
+  // Allocate space in .bss for saving architectural state before jumping
+  // into the test
+  .balign 4
+  .zero 4 * 17 // ra, sp, gp, tp, s0-s11, mtvec
+  .size arch_state_store, .-arch_state_store
+
+  .section .text
+
+run_rvc_test:
+  .globl run_rvc_test
+
+  // Save the architectural state. There is no need to save caller preserved
+  // registers as C calling run_rvc_test will expect them to be clobbered anyway
+  // and act appropriately. RA is the exception as we need to know where to jump
+  // back to after the test is complete. mtvec is saved as the compliance test
+  // environment alters it, other CSRs may also be changed but no adverse
+  // effects have yet been observed from this.
+  la t0, arch_state_store
+
+  sw ra,   0(t0)
+  sw sp,   4(t0)
+  sw gp,   8(t0)
+  sw tp,  12(t0)
+  sw s0,  16(t0)
+  sw s1,  20(t0)
+  sw s2,  24(t0)
+  sw s3,  28(t0)
+  sw s4,  32(t0)
+  sw s5,  36(t0)
+  sw s6,  40(t0)
+  sw s7,  44(t0)
+  sw s8,  48(t0)
+  sw s9,  52(t0)
+  sw s10, 56(t0)
+  sw s11, 60(t0)
+
+  csrr t1, mtvec
+  sw t1, 64(t0)
+
+  // Tail-call to compliance test.
+  //
+  // The test will jump to `end_rvc_test` at the end rather than
+  // return, so we don't want to do a `call`.
+  tail _rvc_start
+
+end_rvc_test:
+  .globl end_rvc_test
+
+  // Restore architectural state.
+  la t0, arch_state_store
+
+  lw ra,   0(t0)
+  lw sp,   4(t0)
+  lw gp,   8(t0)
+  lw tp,  12(t0)
+  lw s0,  16(t0)
+  lw s1,  20(t0)
+  lw s2,  24(t0)
+  lw s3,  28(t0)
+  lw s4,  32(t0)
+  lw s5,  36(t0)
+  lw s6,  40(t0)
+  lw s7,  44(t0)
+  lw s8,  48(t0)
+  lw s9,  52(t0)
+  lw s10, 56(t0)
+  lw s11, 60(t0)
+
+  lw t1, 64(t0)
+  csrw mtvec, t1
+
+  ret
+
+// riscv-compliance loads its own mtvec that will jump to mtvec_handler if the
+// symbol exists. This will only be jumped to in situations where we are not
+// expected to recover.
+mtvec_handler:
+  .globl mtvec_handler
+
+  // Restore gp/sp so handler from the OTTF will work correctly.
+  la t0, arch_state_store
+
+  lw sp,   4(t0)
+  lw gp,   8(t0)
+
+  // Virtual-tail-call into the OTTF's handler.
+  lw t1,  64(t0)
+  jr t1
diff --git a/third_party/riscv-compliance/compliance_main.c b/third_party/riscv-compliance/compliance_main.c
new file mode 100644
index 0000000..07aebbe
--- /dev/null
+++ b/third_party/riscv-compliance/compliance_main.c
@@ -0,0 +1,30 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/test_framework/ottf.h"
+
+// These symbols are provided by the riscv-compliance libraries.
+extern uint32_t begin_signature[];
+extern uint32_t end_signature[];
+
+// This symbol is provided by the genrule that converts the reference
+// output file.
+extern const uint32_t kExpectedSignature[];
+
+// This symbol is defined in compliance_main.S
+extern void run_rvc_test(void);
+
+const test_config_t kTestConfig;
+bool test_main(void) {
+  run_rvc_test();
+
+  ptrdiff_t words = end_signature - begin_signature;
+  CHECK_BUFFER(begin_signature, kExpectedSignature, (size_t)words);
+
+  return true;
+}
diff --git a/third_party/riscv-compliance/defs.bzl b/third_party/riscv-compliance/defs.bzl
new file mode 100644
index 0000000..4bd387c
--- /dev/null
+++ b/third_party/riscv-compliance/defs.bzl
@@ -0,0 +1,147 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+"""Helper macros for generating RISC-V compliance test targets."""
+
+load("//rules:opentitan.bzl", "opentitan_functest", "verilator_params")
+
+def rv_compliance_test(name, arch):
+    test_file = "@riscv-compliance//:riscv-test-suite/{}/src/{}.S".format(arch, name)
+    reference_output = "@riscv-compliance//:riscv-test-suite/{}/references/{}.reference_output".format(arch, name)
+    expected_signature = "{}.expected_signature.S".format(name)
+
+    native.genrule(
+        name = "{}_expected_signature".format(name),
+        srcs = [reference_output],
+        outs = [expected_signature],
+        cmd = """
+        echo "kExpectedSignature:" >> $@
+        echo ".global kExpectedSignature" >> $@
+        perl -pe 's/([a-fA-F0-9]+)/.word 0x$$1/' \
+          $(location {}) \
+          >> $@
+        """.format(reference_output),
+    )
+
+    opentitan_functest(
+        name = name,
+        srcs = [
+            test_file,
+            expected_signature,
+            "compliance_main.c",
+            "compliance_main.S",
+        ],
+        verilator = verilator_params(
+            timeout = "long",
+        ),
+        linkopts = ["-Wl,--no-relax"],
+        deps = [
+            "@riscv-compliance//:riscv-test-env",
+        ],
+    )
+
+TESTS = {
+    "rv32i": [
+        "I-ADD-01",
+        "I-ADDI-01",
+        "I-AND-01",
+        "I-ANDI-01",
+        "I-AUIPC-01",
+        "I-BEQ-01",
+        "I-BGE-01",
+        "I-BGEU-01",
+        "I-BLT-01",
+        "I-BLTU-01",
+        "I-BNE-01",
+        "I-DELAY_SLOTS-01",
+
+        # TODO(lowrisc/opentitan#11876): Failing for unnknown reasons.
+        #"I-EBREAK-01",
+        "I-ECALL-01",
+        "I-ENDIANESS-01",
+        "I-IO-01",
+        "I-JAL-01",
+        "I-JALR-01",
+        "I-LB-01",
+        "I-LBU-01",
+        "I-LH-01",
+        "I-LHU-01",
+        "I-LUI-01",
+        "I-LW-01",
+
+        # TODO(lowrisc/ibex#100): These tests are broken due to flaws in
+        # riscv-compliance rather than Ibex/OpenTitan.
+        #"I-MISALIGN_JMP-01",
+        #"I-MISALIGN_LDST-01",
+
+        # TODO(lowrisc/opentitan#11876): Failing for unnknown reasons.
+        #"I-NOP-01",
+        "I-OR-01",
+        "I-ORI-01",
+        "I-RF_size-01",
+        "I-RF_width-01",
+        "I-RF_x0-01",
+        "I-SB-01",
+        "I-SH-01",
+        "I-SLL-01",
+        "I-SLLI-01",
+        "I-SLT-01",
+        "I-SLTI-01",
+        "I-SLTIU-01",
+        "I-SLTU-01",
+        "I-SRA-01",
+        "I-SRAI-01",
+        "I-SRL-01",
+        "I-SRLI-01",
+        "I-SUB-01",
+        "I-SW-01",
+        "I-XOR-01",
+        "I-XORI-01",
+    ],
+    "rv32im": [
+        "DIV",
+        "DIVU",
+        "MUL",
+        "MULH",
+        "MULHSU",
+        "MULHU",
+        "REM",
+        "REMU",
+    ],
+    "rv32imc": [
+        "C-ADD",
+        "C-ADDI",
+        "C-ADDI16SP",
+        "C-ADDI4SPN",
+        "C-AND",
+        "C-ANDI",
+        "C-BEQZ",
+        "C-BNEZ",
+        "C-J",
+        "C-JAL",
+        "C-JALR",
+        "C-JR",
+        "C-LI",
+        "C-LUI",
+        "C-LW",
+        "C-LWSP",
+        "C-MV",
+        "C-OR",
+        "C-SLLI",
+        "C-SRAI",
+        "C-SRLI",
+        "C-SUB",
+        "C-SW",
+        "C-SWSP",
+        "C-XOR",
+    ],
+    "rv32Zicsr": [
+        "I-CSRRC-01",
+        "I-CSRRCI-01",
+        "I-CSRRS-01",
+        "I-CSRRSI-01",
+        "I-CSRRW-01",
+        "I-CSRRWI-01",
+    ],
+}
diff --git a/third_party/riscv-compliance/deps.bzl b/third_party/riscv-compliance/deps.bzl
new file mode 100644
index 0000000..ad9ee09
--- /dev/null
+++ b/third_party/riscv-compliance/deps.bzl
@@ -0,0 +1,30 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
+
+def riscv_compliance_deps():
+    #new_git_repository(
+    #    name = "riscv-compliance",
+    #    build_file = Label("//third_party/riscv-compliance:BUILD.riscv-compliance.bazel"),
+    #    remote = "https://github.com/riscv/riscv-compliance.git",
+    #    commit = "5a978cfd444d5e640150d46703deda99057b2bbb",
+    #    shallow_since = "1628817357 -0700",
+    #    patches = [
+    #        Label("//third_party/riscv-compliance:0001-Add-configurable-trap-alignment-and-entry-point-to-p.patch")
+    #    ],
+    #    patch_args = ["-p1"],
+    #)
+
+    # TODO(lowRISC/opentitan#11877)
+    # For the time being, use a local repo, but it should really be replaced with
+    # the git repo defined above.
+    #
+    # Everything else in this directory is set up so that we can uncomment the above
+    # without any other changes.
+    native.new_local_repository(
+        name = "riscv-compliance",
+        path = "sw/vendor/riscv_compliance",
+        build_file = "//third_party/riscv-compliance:BUILD.riscv-compliance.bazel",
+    )