| // 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_plic.h" |
| |
| #include "gtest/gtest.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/testing/mock_mmio.h" |
| |
| #include "rv_plic_regs.h" // Generated. |
| |
| namespace dif_plic_unittest { |
| namespace { |
| using mock_mmio::MmioTest; |
| using mock_mmio::MockDevice; |
| using testing::Test; |
| |
| constexpr uint32_t kTarget0 = 0; |
| constexpr uint32_t kFirstIrq = 1; |
| |
| class PlicTest : public Test, public MmioTest { |
| protected: |
| mmio_region_t base_addr_ = dev().region(); |
| dif_plic_t dif_plic_ = { |
| /* base_addr = */ base_addr_, |
| }; |
| }; |
| |
| class InitTest : public PlicTest { |
| protected: |
| void ExpectInitReset() { |
| // Interupt enable multireg. |
| EXPECT_WRITE32(RV_PLIC_IE0_0_REG_OFFSET, 0); |
| EXPECT_WRITE32(RV_PLIC_IE0_1_REG_OFFSET, 0); |
| EXPECT_WRITE32(RV_PLIC_IE0_2_REG_OFFSET, 0); |
| |
| // Level/edge multireg. |
| EXPECT_WRITE32(RV_PLIC_LE_0_REG_OFFSET, 0); |
| EXPECT_WRITE32(RV_PLIC_LE_1_REG_OFFSET, 0); |
| EXPECT_WRITE32(RV_PLIC_LE_2_REG_OFFSET, 0); |
| |
| // Priority registers. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| ptrdiff_t offset = RV_PLIC_PRIO0_REG_OFFSET + (sizeof(uint32_t) * i); |
| EXPECT_WRITE32(offset, 0); |
| } |
| |
| // Target threshold registers. |
| EXPECT_WRITE32(RV_PLIC_THRESHOLD0_REG_OFFSET, 0); |
| |
| // Software interrupt pending register. |
| EXPECT_WRITE32(RV_PLIC_MSIP0_REG_OFFSET, 0); |
| } |
| }; |
| |
| TEST_F(InitTest, NullArgs) { |
| dif_plic_result_t result = dif_plic_init(base_addr_, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(InitTest, Success) { |
| ExpectInitReset(); |
| |
| dif_plic_result_t result = dif_plic_init(base_addr_, &dif_plic_); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| |
| class IrqTests : public PlicTest { |
| protected: |
| struct Register { |
| ptrdiff_t offset; // Register offset from the base. |
| uint8_t last_bit; // Last bit index in the register. |
| }; |
| |
| // Set enable/disable multireg expectations, one bit per call. |
| void ExpectIrqSetTests(const std::vector<Register> ®s, bool enable) { |
| for (const auto ® : regs) { |
| for (uint32_t i = 0; i <= reg.last_bit; ++i) { |
| EXPECT_MASK32(reg.offset, {{i, 0x1, enable}}); |
| } |
| } |
| } |
| |
| // Set multireg get status expectations, one bit per call. |
| void ExpectIrqGetTests(const std::vector<Register> ®s, bool enabled) { |
| for (const auto ® : regs) { |
| for (int i = 0; i <= reg.last_bit; ++i) { |
| uint32_t value = 0x1 << i; |
| |
| if (!enabled) { |
| value = ~value; |
| } |
| |
| EXPECT_READ32(reg.offset, value); |
| } |
| } |
| } |
| }; |
| |
| class IrqEnableSetTest : public IrqTests { |
| protected: |
| // Make sure our tests are up to date. |
| IrqEnableSetTest() { |
| EXPECT_EQ(RV_PLIC_PARAM_NUMTARGET, 1); |
| EXPECT_EQ(registers_.size(), RV_PLIC_IE0_MULTIREG_COUNT); |
| } |
| |
| std::vector<Register> registers_{ |
| { |
| RV_PLIC_IE0_0_REG_OFFSET, RV_PLIC_IE0_0_E_31, |
| }, |
| { |
| RV_PLIC_IE0_1_REG_OFFSET, RV_PLIC_IE0_1_E_63, |
| }, |
| { |
| RV_PLIC_IE0_2_REG_OFFSET, RV_PLIC_IE0_2_E_81, |
| }, |
| }; |
| }; |
| |
| TEST_F(IrqEnableSetTest, NullArgs) { |
| dif_plic_result_t result = |
| dif_plic_irq_enable_set(nullptr, kFirstIrq, kTarget0, kDifPlicEnable); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqEnableSetTest, Target0Enable) { |
| ExpectIrqSetTests(registers_, true); |
| |
| // Enable every IRQ, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_result_t result = |
| dif_plic_irq_enable_set(&dif_plic_, i, kTarget0, kDifPlicEnable); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| TEST_F(IrqEnableSetTest, Target0Disable) { |
| ExpectIrqSetTests(registers_, false); |
| |
| // Disable every bit, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_result_t result = |
| dif_plic_irq_enable_set(&dif_plic_, i, kTarget0, kDifPlicDisable); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| class IrqTriggerTypeSetTest : public IrqTests { |
| protected: |
| // Make sure our tests are up to date. |
| IrqTriggerTypeSetTest() { |
| EXPECT_EQ(registers_.size(), RV_PLIC_LE_MULTIREG_COUNT); |
| } |
| |
| std::vector<Register> registers_{ |
| { |
| RV_PLIC_LE_0_REG_OFFSET, RV_PLIC_LE_0_LE_31, |
| }, |
| { |
| RV_PLIC_LE_1_REG_OFFSET, RV_PLIC_LE_1_LE_63, |
| }, |
| { |
| RV_PLIC_LE_2_REG_OFFSET, RV_PLIC_LE_2_LE_81, |
| }, |
| }; |
| }; |
| |
| TEST_F(IrqTriggerTypeSetTest, NullArgs) { |
| dif_plic_result_t result = |
| dif_plic_irq_trigger_type_set(nullptr, kFirstIrq, kDifPlicEnable); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqTriggerTypeSetTest, Enable) { |
| ExpectIrqSetTests(registers_, true); |
| |
| // Enable every IRQ, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_result_t result = |
| dif_plic_irq_trigger_type_set(&dif_plic_, i, kDifPlicEnable); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| TEST_F(IrqTriggerTypeSetTest, Disable) { |
| ExpectIrqSetTests(registers_, false); |
| |
| // Enable every IRQ, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_result_t result = |
| dif_plic_irq_trigger_type_set(&dif_plic_, i, kDifPlicDisable); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| class IrqPrioritySetTest : public PlicTest {}; |
| |
| TEST_F(IrqPrioritySetTest, NullArgs) { |
| dif_plic_result_t result = |
| dif_plic_irq_priority_set(nullptr, kFirstIrq, kDifPlicMaxPriority); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqPrioritySetTest, PriorityInvalid) { |
| dif_plic_result_t result = |
| dif_plic_irq_priority_set(nullptr, kFirstIrq, kDifPlicMaxPriority + 1); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqPrioritySetTest, Success) { |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| // Set expectations for every priority set call. |
| ptrdiff_t offset = RV_PLIC_PRIO0_REG_OFFSET + (sizeof(uint32_t) * i); |
| EXPECT_WRITE32(offset, kDifPlicMaxPriority); |
| |
| // Set every priority register to MAX priority. |
| dif_plic_result_t result = |
| dif_plic_irq_priority_set(&dif_plic_, i, kDifPlicMaxPriority); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| class TargetThresholdSetTest : public PlicTest { |
| protected: |
| // Make sure our tests are up to date. |
| TargetThresholdSetTest() { EXPECT_EQ(RV_PLIC_PARAM_NUMTARGET, 1); } |
| }; |
| |
| TEST_F(TargetThresholdSetTest, Target0NullArgs) { |
| dif_plic_result_t result = |
| dif_plic_target_threshold_set(nullptr, kTarget0, kDifPlicMaxPriority); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(TargetThresholdSetTest, Target0PriorityInvalid) { |
| dif_plic_result_t result = dif_plic_target_threshold_set( |
| &dif_plic_, kTarget0, kDifPlicMaxPriority + 1); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(TargetThresholdSetTest, Target0Success) { |
| EXPECT_WRITE32(RV_PLIC_THRESHOLD0_REG_OFFSET, kDifPlicMaxPriority); |
| |
| dif_plic_result_t result = |
| dif_plic_target_threshold_set(&dif_plic_, kTarget0, kDifPlicMaxPriority); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| |
| class IrqPendingStatusGetTest : public IrqTests { |
| protected: |
| // Make sure our tests are up to date. |
| IrqPendingStatusGetTest() { |
| EXPECT_EQ(registers_.size(), RV_PLIC_IP_MULTIREG_COUNT); |
| } |
| |
| std::vector<Register> registers_{ |
| { |
| RV_PLIC_IP_0_REG_OFFSET, RV_PLIC_IP_0_P_31, |
| }, |
| { |
| RV_PLIC_IP_1_REG_OFFSET, RV_PLIC_IP_1_P_63, |
| }, |
| { |
| RV_PLIC_IP_2_REG_OFFSET, RV_PLIC_IP_2_P_81, |
| }, |
| }; |
| }; |
| |
| TEST_F(IrqPendingStatusGetTest, NullArgs) { |
| dif_plic_flag_t status; |
| dif_plic_result_t result = |
| dif_plic_irq_pending_status_get(nullptr, kFirstIrq, &status); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_pending_status_get(&dif_plic_, kFirstIrq, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_pending_status_get(nullptr, kFirstIrq, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqPendingStatusGetTest, Enabled) { |
| ExpectIrqGetTests(registers_, true); |
| |
| // Get status of every IRQ, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_flag_t status = kDifPlicUnset; |
| dif_plic_result_t result = |
| dif_plic_irq_pending_status_get(&dif_plic_, i, &status); |
| EXPECT_EQ(result, kDifPlicOk); |
| EXPECT_EQ(status, kDifPlicSet); |
| } |
| } |
| |
| TEST_F(IrqPendingStatusGetTest, Disabled) { |
| ExpectIrqGetTests(registers_, false); |
| |
| // Get status of every IRQ, one at a time. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_flag_t status = kDifPlicSet; |
| dif_plic_result_t result = |
| dif_plic_irq_pending_status_get(&dif_plic_, i, &status); |
| EXPECT_EQ(result, kDifPlicOk); |
| EXPECT_EQ(status, kDifPlicUnset); |
| } |
| } |
| |
| class IrqClaimTest : public PlicTest { |
| protected: |
| // Make sure our tests are up to date. |
| IrqClaimTest() { EXPECT_EQ(RV_PLIC_PARAM_NUMTARGET, 1); } |
| }; |
| |
| TEST_F(IrqClaimTest, Target0NullArgs) { |
| dif_plic_irq_id_t data; |
| dif_plic_result_t result = dif_plic_irq_claim(nullptr, kTarget0, &data); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_claim(&dif_plic_, kTarget0, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_claim(nullptr, kTarget0, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqClaimTest, Target0Success) { |
| // Set expectations for every claim call. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| EXPECT_READ32(RV_PLIC_CC0_REG_OFFSET, i); |
| } |
| |
| // Claim every IRQ, one per a call. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_irq_id_t data; |
| dif_plic_result_t result = dif_plic_irq_claim(&dif_plic_, kTarget0, &data); |
| EXPECT_EQ(result, kDifPlicOk); |
| EXPECT_EQ(data, i); |
| } |
| } |
| |
| class IrqCompleteTest : public PlicTest { |
| protected: |
| // Make sure our tests are up to date. |
| IrqCompleteTest() { EXPECT_EQ(RV_PLIC_PARAM_NUMTARGET, 1); } |
| }; |
| |
| TEST_F(IrqCompleteTest, Target0NullArgs) { |
| dif_plic_irq_id_t data; |
| dif_plic_result_t result = dif_plic_irq_complete(nullptr, kTarget0, &data); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_complete(&dif_plic_, kTarget0, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| |
| result = dif_plic_irq_complete(nullptr, kTarget0, nullptr); |
| EXPECT_EQ(result, kDifPlicBadArg); |
| } |
| |
| TEST_F(IrqCompleteTest, Target0Success) { |
| // Set expectations for every complete call. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| EXPECT_WRITE32(RV_PLIC_CC0_REG_OFFSET, i); |
| } |
| |
| // Complete all of the IRQs. |
| for (int i = 0; i < RV_PLIC_PARAM_NUMSRC; ++i) { |
| dif_plic_irq_id_t data = i; |
| dif_plic_result_t result = |
| dif_plic_irq_complete(&dif_plic_, kTarget0, &data); |
| EXPECT_EQ(result, kDifPlicOk); |
| } |
| } |
| |
| } // namespace |
| } // namespace dif_plic_unittest |