[hmac_enc, tests] Introduce hmac_enc chip level IRQ test
https://docs.opentitan.org/sw/device/tests/#ip-integration
chip_sw_hmac_enc
This test simply ensures that "empty" and "done" interrupts fire
up when appropriate conditions are met. The test only partially
satisfies the chip_sw_hmac_enc requirements, and requires a
separate test to verify the digest.
Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index 28f93ec..3ddc228 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -1622,7 +1622,7 @@
the HMAC done and FIFO empty interrupts as a part of this test.
'''
milestone: V2
- tests: []
+ tests: ["chip_sw_hmac_enc_irq"]
}
{
name: chip_sw_hmac_idle
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 938e5d8..ce42fe5 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -395,6 +395,12 @@
run_opts: ["+sw_test_timeout_ns=22000000"]
}
{
+ name: chip_sw_hmac_enc_irq_test
+ uvm_test_seq: chip_sw_base_vseq
+ sw_images: ["sw/device/tests/hmac_enc_irq_test:1"]
+ en_run_modes: ["sw_test_mode_test_rom"]
+ }
+ {
name: chip_sw_kmac_mode_cshake_test
uvm_test_seq: chip_sw_base_vseq
sw_images: ["sw/device/tests/kmac_mode_cshake_test:1"]
diff --git a/sw/device/tests/hmac_enc_irq_test.c b/sw/device/tests/hmac_enc_irq_test.c
new file mode 100644
index 0000000..a40864e
--- /dev/null
+++ b/sw/device/tests/hmac_enc_irq_test.c
@@ -0,0 +1,146 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
+
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_hmac.h"
+#include "sw/device/lib/dif/dif_rv_plic.h"
+#include "sw/device/lib/irq.h"
+#include "sw/device/lib/runtime/ibex.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/rv_plic_testutils.h"
+#include "sw/device/lib/testing/test_framework/ottf.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "sw/device/lib/testing/autogen/isr_testutils.h"
+
+/**
+ * HMAC done 50uS timeout, which should be enough even for a 100kHz device.
+ *
+ * https://docs.opentitan.org/hw/ip/hmac/doc/
+ * Final hash calculation takes 360 cycles, which consists of one
+ * block compression and extra HMAC computation.
+ */
+const uint32_t kHmacEncFinishTimeoutUsec = 50;
+
+/**
+ * FIFO empty 10uS timeout, which should be enough even for a 100kHz device.
+ *
+ * https://docs.opentitan.org/hw/ip/hmac/doc/
+ * Single HMAC block compression takes 80 cycles.
+ */
+const uint32_t kHmacEncEmptyTimeoutUsec = 10;
+
+static plic_isr_ctx_t plic_ctx = {
+ .hart_id = kTopEarlgreyPlicTargetIbex0,
+};
+
+static dif_hmac_t hmac;
+static top_earlgrey_plic_peripheral_t peripheral_serviced;
+static dif_hmac_irq_t irq_serviced;
+static hmac_isr_ctx_t hmac_ctx = {
+ .hmac = &hmac,
+ .plic_hmac_start_irq_id = kTopEarlgreyPlicIrqIdHmacHmacDone,
+ .is_only_irq = false,
+};
+
+const test_config_t kTestConfig;
+
+static const dif_hmac_transaction_t kHmacTransactionConfig = {
+ .digest_endianness = kDifHmacEndiannessLittle,
+ .message_endianness = kDifHmacEndiannessLittle,
+};
+
+/**
+ * External ISR.
+ *
+ * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
+ * line to the CPU, which results in a call to this OTTF ISR. This ISR
+ * overrides the default OTTF implementation.
+ */
+void ottf_external_isr(void) {
+ isr_testutils_hmac_isr(plic_ctx, hmac_ctx, &peripheral_serviced,
+ &irq_serviced);
+}
+
+/**
+ * Enables interrupts required by this test.
+ */
+static void enable_irqs(void) {
+ mmio_region_t base_addr =
+ mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR);
+ CHECK_DIF_OK(dif_rv_plic_init(base_addr, plic_ctx.rv_plic));
+
+ // Enable interrupts in HMAC IP.
+ CHECK_DIF_OK(dif_hmac_irq_set_enabled(hmac_ctx.hmac, kDifHmacIrqHmacDone,
+ kDifToggleEnabled));
+ CHECK_DIF_OK(dif_hmac_irq_set_enabled(hmac_ctx.hmac, kDifHmacIrqFifoEmpty,
+ kDifToggleEnabled));
+
+ // Enable interrupts in PLIC.
+ rv_plic_testutils_irq_range_enable(plic_ctx.rv_plic, plic_ctx.hart_id,
+ kTopEarlgreyPlicIrqIdHmacHmacDone,
+ kTopEarlgreyPlicIrqIdHmacFifoEmpty);
+
+ // Enable interrupts in Ibex.
+ irq_global_ctrl(true);
+ irq_external_ctrl(true);
+}
+
+/**
+ * Read and compare the length of the message in the HMAC engine to the length
+ * of the message sent in bits.
+ */
+static void check_message_length(uint64_t expected_sent_bits) {
+ uint64_t sent_bits;
+ CHECK_DIF_OK(dif_hmac_get_message_length(&hmac, &sent_bits));
+
+ // 64bit formatting is not supported, so split into hi and lo hex 32bit
+ // values. These should appear as 64bit hex values in the debug output.
+ CHECK(expected_sent_bits == sent_bits,
+ "Message length mismatch. "
+ "Expected 0x%08x%08x bits but got 0x%08x%08x bits.",
+ (uint32_t)(expected_sent_bits >> 32), (uint32_t)expected_sent_bits,
+ (uint32_t)(sent_bits >> 32), (uint32_t)sent_bits);
+}
+
+bool test_main() {
+ mmio_region_t base_addr = mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR);
+ CHECK_DIF_OK(dif_hmac_init(base_addr, &hmac));
+
+ enable_irqs();
+
+ // The purpose of this test is to ensure that HMAC empty and done interrupts
+ // assert when the conditions are met. Digest is not verified by this
+ // test, which means that a "dummy" data will suffice.
+ size_t sent_bytes;
+ char data[4] = {0xaa, 0xbb, 0xcc, 0xdd};
+ CHECK_DIF_OK(dif_hmac_mode_sha256_start(&hmac, kHmacTransactionConfig));
+ hmac_ctx.expected_irq = kDifHmacIrqFifoEmpty;
+ CHECK_DIF_OK(
+ dif_hmac_fifo_push(&hmac, &data[0], ARRAYSIZE(data), &sent_bytes));
+ check_message_length(32);
+
+ // Spin waiting for the "empty" interrupt.
+ IBEX_SPIN_FOR(irq_serviced == hmac_ctx.expected_irq,
+ kHmacEncEmptyTimeoutUsec);
+
+ // Race conditions could result in a stale value due to `hmac_empty_irq`
+ // being set in the ISR, however, in practice that does not matter.
+ hmac_ctx.expected_irq = kDifHmacIrqHmacDone;
+ CHECK_DIF_OK(dif_hmac_process(&hmac));
+ LOG_INFO("Waiting for HMAC to finish");
+
+ // Spin waiting for the "done" interrupt.
+ IBEX_SPIN_FOR(irq_serviced == hmac_ctx.expected_irq,
+ kHmacEncFinishTimeoutUsec);
+
+ // Finish the HMAC operation.
+ dif_hmac_digest_t dummy_digest;
+ CHECK_DIF_OK(dif_hmac_finish(&hmac, &dummy_digest));
+
+ return true;
+}
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index f730c75..fc665c4 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -907,6 +907,29 @@
}
}
+hmac_enc_irq_test_lib = declare_dependency(
+ link_with: static_library(
+ 'hmac_enc_irq_test_lib',
+ sources: ['hmac_enc_irq_test.c'],
+ dependencies: [
+ sw_lib_dif_hmac,
+ sw_lib_dif_rv_plic,
+ sw_lib_irq,
+ sw_lib_mmio,
+ sw_lib_runtime_ibex,
+ sw_lib_runtime_log,
+ sw_lib_testing_isr_testutils,
+ sw_lib_testing_rv_plic_testutils,
+ top_earlgrey,
+ ],
+ ),
+)
+sw_tests += {
+ 'hmac_enc_irq_test': {
+ 'library': hmac_enc_irq_test_lib,
+ }
+}
+
###############################################################################
# Auto-generated tests
###############################################################################