[dif, rv_core_ibex] Add address translation and unittest

Signed-off-by: Douglas Reis <doreis@lowrisc.org>
diff --git a/sw/device/lib/base/bitfield.c b/sw/device/lib/base/bitfield.c
index 2cf1204..66c65bd 100644
--- a/sw/device/lib/base/bitfield.c
+++ b/sw/device/lib/base/bitfield.c
@@ -32,3 +32,4 @@
 extern int32_t bitfield_popcount32(uint32_t bitfield);
 extern int32_t bitfield_parity32(uint32_t bitfield);
 extern uint32_t bitfield_byteswap32(uint32_t bitfield);
+extern bool bitfield_is_power_of_two32(uint32_t bitfield);
diff --git a/sw/device/lib/base/bitfield.h b/sw/device/lib/base/bitfield.h
index cab6086..a105f59 100644
--- a/sw/device/lib/base/bitfield.h
+++ b/sw/device/lib/base/bitfield.h
@@ -293,6 +293,18 @@
   return __builtin_bswap32(bitfield);
 }
 
+/**
+ * Check whether the bitfield value is power of two aligned.
+ *
+ * Zero will also return false.
+ *
+ * @param bitfield Value to be verified.
+ * @return True if bitfield is power of two, otherwise false.
+ */
+inline bool bitfield_is_power_of_two32(uint32_t bitfield) {
+  return bitfield != 0 && (bitfield & (bitfield - 1)) == 0;
+}
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/sw/device/lib/dif/BUILD b/sw/device/lib/dif/BUILD
index 3e5ee1e..218033a 100644
--- a/sw/device/lib/dif/BUILD
+++ b/sw/device/lib/dif/BUILD
@@ -826,8 +826,10 @@
     srcs = [
         "autogen/dif_rv_core_ibex_autogen.c",
         "autogen/dif_rv_core_ibex_autogen.h",
+        "dif_rv_core_ibex.c",
     ],
     hdrs = [
+        "dif_rv_core_ibex.h",
     ],
     deps = [
         ":base",
@@ -841,6 +843,7 @@
     name = "rv_core_ibex_unittest",
     srcs = [
         "autogen/dif_rv_core_ibex_autogen_unittest.cc",
+        "dif_rv_core_ibex_unittest.cc",
     ],
     deps = [
         ":rv_core_ibex",
diff --git a/sw/device/lib/dif/dif_rv_core_ibex.c b/sw/device/lib/dif/dif_rv_core_ibex.c
new file mode 100644
index 0000000..5ff1b7e
--- /dev/null
+++ b/sw/device/lib/dif/dif_rv_core_ibex.c
@@ -0,0 +1,163 @@
+// 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/dif/dif_rv_core_ibex.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/base/mmio.h"
+
+// #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "rv_core_ibex_regs.h"
+
+typedef struct ibex_addr_translation_regs {
+  uint32_t ibus_maching;
+  uint32_t ibus_remap;
+  uint32_t ibus_en;
+  uint32_t ibus_lock;
+  uint32_t dbus_maching;
+  uint32_t dbus_remap;
+  uint32_t dbus_en;
+  uint32_t dbus_lock;
+} ibex_addr_translation_regs_t;
+
+static const ibex_addr_translation_regs_t
+    kRegsMap[kDifRvCoreIbexAddrTranslationSlotCount] = {
+        [kDifRvCoreIbexAddrTranslationSlot_0] =
+            {
+                .ibus_maching = RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET,
+                .ibus_remap = RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET,
+                .ibus_en = RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET,
+                .ibus_lock = RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET,
+                .dbus_maching = RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET,
+                .dbus_remap = RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET,
+                .dbus_en = RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET,
+                .dbus_lock = RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET,
+            },
+        [kDifRvCoreIbexAddrTranslationSlot_1] =
+            {
+                .ibus_maching = RV_CORE_IBEX_IBUS_ADDR_MATCHING_1_REG_OFFSET,
+                .ibus_remap = RV_CORE_IBEX_IBUS_REMAP_ADDR_1_REG_OFFSET,
+                .ibus_en = RV_CORE_IBEX_IBUS_ADDR_EN_1_REG_OFFSET,
+                .ibus_lock = RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET,
+                .dbus_maching = RV_CORE_IBEX_DBUS_ADDR_MATCHING_1_REG_OFFSET,
+                .dbus_remap = RV_CORE_IBEX_DBUS_REMAP_ADDR_1_REG_OFFSET,
+                .dbus_en = RV_CORE_IBEX_DBUS_ADDR_EN_1_REG_OFFSET,
+                .dbus_lock = RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET,
+            },
+};
+
+/**
+ * Convert the region address and size into a natural power of two address.
+ *
+ * @param addr Region start address.
+ * @param size region size.
+ * @return Napot address
+ */
+static uint32_t to_napot(uint32_t addr, size_t size) {
+  return addr | (size - 1) >> 1;
+}
+
+/**
+ * Split a natural power of two address into a start address and size.
+ *
+ * @param napot Address formated in NAPOT.
+ * @param size  Pointer to receive the region size.
+ * @return The region start address.
+ */
+static uint32_t from_napot(uint32_t napot, size_t *size) {
+  for (size_t i = 1; i < sizeof(uint32_t) * 8; ++i) {
+    uint32_t ref = (1u << i) - 1;
+    if ((napot & ref) == ref >> 1) {
+      *size = 1u << i;
+      break;
+    }
+  }
+  return napot & ~((*size - 1) >> 1);
+}
+
+dif_result_t dif_rv_core_ibex_configure_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot,
+    dif_rv_core_ibex_addr_translation_region_t region) {
+  if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount) {
+    return kDifBadArg;
+  }
+
+  if (!bitfield_is_power_of_two32(region.dbus.size) ||
+      !bitfield_is_power_of_two32(region.ibus.size)) {
+    return kDifBadArg;
+  }
+
+  const ibex_addr_translation_regs_t regs = kRegsMap[slot];
+
+  if (mmio_region_read32(rv_core_ibex->base_addr, regs.dbus_lock) == 0 ||
+      mmio_region_read32(rv_core_ibex->base_addr, regs.ibus_lock) == 0) {
+    return kDifLocked;
+  }
+
+  uint32_t mask = to_napot(region.ibus.matching_addr, region.ibus.size);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.ibus_maching, mask);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.ibus_remap,
+                      region.ibus.remap_addr);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.ibus_en, 1);
+
+  mask = to_napot(region.dbus.matching_addr, region.dbus.size);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.dbus_maching, mask);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.dbus_remap,
+                      region.dbus.remap_addr);
+  mmio_region_write32(rv_core_ibex->base_addr, regs.dbus_en, 1);
+
+  return kDifOk;
+}
+
+dif_result_t dif_rv_core_ibex_read_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot,
+    dif_rv_core_ibex_addr_translation_region_t *region) {
+  if (rv_core_ibex == NULL || region == NULL ||
+      slot >= kDifRvCoreIbexAddrTranslationSlotCount) {
+    return kDifBadArg;
+  }
+
+  const ibex_addr_translation_regs_t regs = kRegsMap[slot];
+
+  uint32_t reg = mmio_region_read32(rv_core_ibex->base_addr, regs.ibus_maching);
+  region->ibus.matching_addr = from_napot(reg, &region->ibus.size);
+
+  region->ibus.remap_addr =
+      mmio_region_read32(rv_core_ibex->base_addr, regs.ibus_remap);
+
+  reg = mmio_region_read32(rv_core_ibex->base_addr, regs.dbus_maching);
+  region->dbus.matching_addr = from_napot(reg, &region->dbus.size);
+
+  region->dbus.remap_addr =
+      mmio_region_read32(rv_core_ibex->base_addr, regs.dbus_remap);
+
+  return kDifOk;
+}
+
+dif_result_t dif_rv_core_ibex_lock_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot) {
+  if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount) {
+    return kDifBadArg;
+  }
+
+  const ibex_addr_translation_regs_t regs = kRegsMap[slot];
+
+  // Only locks in case it is not locked already.
+  if (mmio_region_read32(rv_core_ibex->base_addr, regs.dbus_lock) == 1) {
+    mmio_region_write32(rv_core_ibex->base_addr, regs.dbus_lock, 0);
+  }
+
+  if (mmio_region_read32(rv_core_ibex->base_addr, regs.ibus_lock) == 1) {
+    mmio_region_write32(rv_core_ibex->base_addr, regs.ibus_lock, 0);
+  }
+
+  return kDifOk;
+}
diff --git a/sw/device/lib/dif/dif_rv_core_ibex.h b/sw/device/lib/dif/dif_rv_core_ibex.h
new file mode 100644
index 0000000..9753cd6
--- /dev/null
+++ b/sw/device/lib/dif/dif_rv_core_ibex.h
@@ -0,0 +1,122 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_CORE_IBEX_H_
+#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_CORE_IBEX_H_
+
+#include <stdint.h>
+
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_base.h"
+
+#include "sw/device/lib/dif/autogen/dif_rv_core_ibex_autogen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * Address translation slot.
+ */
+typedef enum dif_rv_core_ibex_addr_translation_slot {
+  kDifRvCoreIbexAddrTranslationSlot_0,
+  kDifRvCoreIbexAddrTranslationSlot_1,
+  kDifRvCoreIbexAddrTranslationSlotCount,
+} dif_rv_core_ibex_addr_translation_slot_t;
+
+/**
+ * Address tranlation matching region.
+ *
+ * The value programmed is done at power-of-2 alignment. For example, if the
+ * intended matching region is 0x8000_0000 to 0x8000_FFFF, the value
+ * programmed is 0x8000_7FFF.
+ *
+ * The value programmed can be determined from the translation granule. Assume
+ * the user wishes to translate a specific 64KB block to a different address:
+ * 64KB has a hex value of 0x10000. Subtract 1 from this value and then right
+ * shift by one to obtain 0x7FFF. This value is then logically OR'd with the
+ * upper address bits that would select which 64KB to translate.
+ */
+typedef struct dif_rv_core_ibex_addr_translation_pair {
+  /**
+   * Matching address (Virtual address).
+   * When an incoming transaction matches the matching
+   * region, it is redirected to the new address. If a transaction does not
+   * match, then it is directly passed through.
+   */
+  uintptr_t matching_addr;
+
+  /**
+   * Remap address (Physical address).
+   * The region where the matched transtaction will be
+   * redirected to.
+   */
+  uintptr_t remap_addr;
+
+  /**
+   * Address region size.
+   */
+  size_t size;
+} dif_rv_core_ibex_addr_translation_pair_t;
+
+/**
+ * Addresses translation region.
+ */
+typedef struct dif_rv_core_ibex_addr_translation_region {
+  /**
+   * Region representing the instruction bus.
+   */
+  dif_rv_core_ibex_addr_translation_pair_t ibus;
+
+  /**
+   * Region representing the data bus.
+   */
+  dif_rv_core_ibex_addr_translation_pair_t dbus;
+} dif_rv_core_ibex_addr_translation_region_t;
+
+/**
+ * Configure the instruction and data bus in the address translation `slot`.
+ *
+ * @param rv_core_ibex Handle.
+ * @param slot   Slot to be used.
+ * @param region Dbus and Ibus addresses.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_rv_core_ibex_configure_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot,
+    dif_rv_core_ibex_addr_translation_region_t region);
+
+/**
+ *
+ * @param rv_core_ibex Handle.
+ * @param slot Slot to be read.
+ * @param[out] region Pointer to receive the Dbus and Ibus addresses.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_rv_core_ibex_read_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot,
+    dif_rv_core_ibex_addr_translation_region_t *region);
+
+/**
+ * Lock the `slot` registers. Once locked it can no longer be unlocked until the
+ * next system reset.
+ *
+ * @param rv_core_ibex Handle.
+ * @param slot Slot to be locked.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_rv_core_ibex_lock_addr_translation(
+    const dif_rv_core_ibex_t *rv_core_ibex,
+    dif_rv_core_ibex_addr_translation_slot_t slot);
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_CORE_IBEX_H_
diff --git a/sw/device/lib/dif/dif_rv_core_ibex_unittest.cc b/sw/device/lib/dif/dif_rv_core_ibex_unittest.cc
new file mode 100644
index 0000000..6a8d6b1
--- /dev/null
+++ b/sw/device/lib/dif/dif_rv_core_ibex_unittest.cc
@@ -0,0 +1,265 @@
+// 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/dif/dif_rv_core_ibex.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/base/testing/mock_mmio.h"
+#include "sw/device/lib/dif/dif_test_base.h"
+
+extern "C" {
+#include "rv_core_ibex_regs.h"  // Generated.
+}  // extern "C"
+
+// We define global namespace == and << to make `dif_i2c_timing_params_t` work
+// nicely with EXPECT_EQ.
+bool operator==(const dif_rv_core_ibex_addr_translation_region_t a,
+                const dif_rv_core_ibex_addr_translation_region_t b) {
+  return memcmp(&a, &b, sizeof(dif_rv_core_ibex_addr_translation_region_t)) ==
+         0;
+}
+
+std::ostream &operator<<(
+    std::ostream &os,
+    const dif_rv_core_ibex_addr_translation_region_t &region) {
+  return os << "{\n"
+            << "ibus = {\n"
+            << "  .matching_addr = " << region.ibus.matching_addr << ",\n"
+            << "  .remap_addr = " << region.ibus.remap_addr << ",\n"
+            << "  .size = " << region.ibus.size << ",\n"
+            << "},\n"
+            << "dbus = {\n"
+            << "  .matching_addr = " << region.dbus.matching_addr << ",\n"
+            << "  .remap_addr = " << region.dbus.remap_addr << ",\n"
+            << "  .size = " << region.dbus.size << ",\n"
+            << "},\n"
+            << "}";
+}
+
+namespace dif_rv_core_ibex_test {
+using mock_mmio::MmioTest;
+using mock_mmio::MockDevice;
+using testing::Test;
+
+// Base class for the rest fixtures in this file.
+class RvCoreIbexTest : public testing::Test, public mock_mmio::MmioTest {};
+
+// Base class for the rest of the tests in this file, provides a
+// `dif_aes_t` instance.
+class RvCoreIbexTestInitialized : public RvCoreIbexTest {
+ protected:
+  dif_rv_core_ibex_t ibex_;
+
+  RvCoreIbexTestInitialized() {
+    EXPECT_DIF_OK(dif_rv_core_ibex_init(dev().region(), &ibex_));
+  }
+};
+
+class AddressTranslationTest : public RvCoreIbexTestInitialized {
+ protected:
+  static constexpr dif_rv_core_ibex_addr_translation_region_t kRegion = {
+      .ibus =
+          {
+              .matching_addr = 0x9000000,
+              .remap_addr = 0x2000000,
+              .size = 0x8000,
+          },
+      .dbus =
+          {
+              .matching_addr = 0x9000000,
+              .remap_addr = 0x2000000,
+              .size = 0x8000,
+          },
+  };
+
+  uint32_t Napot(uint32_t addr, size_t size) { return addr | (size - 1) >> 1; }
+};
+constexpr dif_rv_core_ibex_addr_translation_region_t
+    AddressTranslationTest::kRegion;
+
+TEST_F(AddressTranslationTest, Slot0Success) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 1);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 1);
+
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET, 0x9003fff);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET,
+                 kRegion.ibus.remap_addr);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET, 1);
+
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET, 0x9003fff);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET,
+                 kRegion.dbus.remap_addr);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET, 1);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_configure_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, kRegion));
+}
+
+TEST_F(AddressTranslationTest, Slot1Success) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 1);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET, 1);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_MATCHING_1_REG_OFFSET, 0x9003fff);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_REMAP_ADDR_1_REG_OFFSET,
+                 kRegion.ibus.remap_addr);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_EN_1_REG_OFFSET, 1);
+
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_MATCHING_1_REG_OFFSET, 0x9003fff);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_REMAP_ADDR_1_REG_OFFSET,
+                 kRegion.dbus.remap_addr);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_EN_1_REG_OFFSET, 1);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_configure_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_1, kRegion));
+}
+
+TEST_F(AddressTranslationTest, PowerOfTwoAlignmentSuccess) {
+  dif_rv_core_ibex_addr_translation_region_t region = kRegion;
+
+  region.ibus.matching_addr = 0x8000000;
+  region.dbus.matching_addr = 0x8000000;
+
+  for (size_t i = 0; i < (sizeof(uint32_t) * 8) - 1; ++i) {
+    region.ibus.size = 1u << i;
+    region.dbus.size = 1u << i;
+
+    EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 1);
+    EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 1);
+
+    EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET,
+                   Napot(region.ibus.matching_addr, region.ibus.size));
+    EXPECT_WRITE32(RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET,
+                   region.ibus.remap_addr);
+    EXPECT_WRITE32(RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET, 1);
+
+    EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET,
+                   Napot(region.dbus.matching_addr, region.dbus.size));
+    EXPECT_WRITE32(RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET,
+                   region.dbus.remap_addr);
+    EXPECT_WRITE32(RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET, 1);
+
+    EXPECT_DIF_OK(dif_rv_core_ibex_configure_addr_translation(
+        &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, region));
+  }
+}
+
+TEST_F(AddressTranslationTest, ReadSlot0Success) {
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET, 0x9003fff);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET,
+                kRegion.ibus.remap_addr);
+
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET, 0x9003fff);
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET,
+                kRegion.dbus.remap_addr);
+
+  dif_rv_core_ibex_addr_translation_region_t region;
+  EXPECT_DIF_OK(dif_rv_core_ibex_read_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, &region));
+
+  EXPECT_EQ(region, kRegion);
+}
+
+TEST_F(AddressTranslationTest, LockSlot0Success) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 1);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 0);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 1);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 0);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_lock_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_0));
+}
+
+TEST_F(AddressTranslationTest, LockSlot0LockedSuccess) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 0);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 0);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_lock_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_0));
+}
+
+TEST_F(AddressTranslationTest, LockSlot1Success) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 1);
+  EXPECT_WRITE32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 0);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET, 1);
+  EXPECT_WRITE32(RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET, 0);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_lock_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_1));
+}
+
+TEST_F(AddressTranslationTest, LockSlot1LockedSuccess) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 0);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET, 0);
+
+  EXPECT_DIF_OK(dif_rv_core_ibex_lock_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_1));
+}
+
+TEST_F(AddressTranslationTest, BadArg) {
+  dif_rv_core_ibex_addr_translation_region_t region;
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_configure_addr_translation(
+      nullptr, kDifRvCoreIbexAddrTranslationSlot_1, region));
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_configure_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlotCount, region));
+
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_read_addr_translation(
+      nullptr, kDifRvCoreIbexAddrTranslationSlot_1, &region));
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_read_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlotCount, &region));
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_read_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_1, nullptr));
+
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_lock_addr_translation(
+      nullptr, kDifRvCoreIbexAddrTranslationSlot_1));
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_lock_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlotCount));
+}
+
+TEST_F(AddressTranslationTest, Slot0DbusLocked) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 0);
+
+  EXPECT_EQ(dif_rv_core_ibex_configure_addr_translation(
+                &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, kRegion),
+            kDifLocked);
+}
+
+TEST_F(AddressTranslationTest, Slot0IbusLocked) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET, 1);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET, 0);
+
+  EXPECT_EQ(dif_rv_core_ibex_configure_addr_translation(
+                &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, kRegion),
+            kDifLocked);
+}
+
+TEST_F(AddressTranslationTest, Slot1DbusLocked) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 0);
+
+  EXPECT_EQ(dif_rv_core_ibex_configure_addr_translation(
+                &ibex_, kDifRvCoreIbexAddrTranslationSlot_1, kRegion),
+            kDifLocked);
+}
+
+TEST_F(AddressTranslationTest, Slot1IbusLocked) {
+  EXPECT_READ32(RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET, 1);
+  EXPECT_READ32(RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET, 0);
+
+  EXPECT_EQ(dif_rv_core_ibex_configure_addr_translation(
+                &ibex_, kDifRvCoreIbexAddrTranslationSlot_1, kRegion),
+            kDifLocked);
+}
+
+TEST_F(AddressTranslationTest, NotPowerOfTwo) {
+  dif_rv_core_ibex_addr_translation_region_t region = kRegion;
+  region.dbus.size += 0x20;
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_configure_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_1, region));
+
+  region.dbus.size -= 0x20;
+  region.ibus.size += 0x20;
+  EXPECT_DIF_BADARG(dif_rv_core_ibex_configure_addr_translation(
+      &ibex_, kDifRvCoreIbexAddrTranslationSlot_0, region));
+}
+
+}  // namespace dif_rv_core_ibex_test