blob: 138255889ade5710cf07c69d45bad5e886582b91 [file] [log] [blame]
// 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_gpio.h"
#include <limits>
#include "gtest/gtest.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/base/mock_mmio.h"
#include "sw/device/lib/dif/dif_test_base.h"
#include "gpio_regs.h" // Generated
namespace dif_gpio_unittest {
namespace {
// Convenience constants and functions
constexpr uint32_t kAllOnes = std::numeric_limits<uint32_t>::max();
uint32_t AllZerosExcept(uint32_t index) { return 1 << index; }
uint32_t AllOnesExcept(uint32_t index) { return ~AllZerosExcept(index); }
// Base class for the test fixtures in this file.
class GpioTest : public testing::Test, public mock_mmio::MmioTest {};
// Base class for the rest of the tests in this file, provides a
// `dif_gpio_t` instance.
class GpioTestInitialized : public GpioTest {
protected:
const dif_gpio_t gpio_ = {.base_addr = dev().region()};
};
// Reset tests
class ResetTest : public GpioTestInitialized {};
TEST_F(ResetTest, BadArgs) { EXPECT_DIF_BADARG(dif_gpio_reset(nullptr)); }
TEST_F(ResetTest, Reset) {
EXPECT_WRITE32(GPIO_INTR_ENABLE_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_DIRECT_OE_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_DIRECT_OUT_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_STATE_REG_OFFSET, kAllOnes);
EXPECT_DIF_OK(dif_gpio_reset(&gpio_));
}
// Read tests
class ReadTest : public GpioTestInitialized {};
TEST_F(ReadTest, BadArgs) {
dif_gpio_state_t out_arg_uint32_t;
bool out_arg_bool;
EXPECT_DIF_BADARG(dif_gpio_read_all(nullptr, &out_arg_uint32_t));
EXPECT_DIF_BADARG(dif_gpio_read_all(&gpio_, nullptr));
EXPECT_DIF_BADARG(dif_gpio_read_all(nullptr, nullptr));
EXPECT_DIF_BADARG(dif_gpio_read(nullptr, 0, &out_arg_bool));
EXPECT_DIF_BADARG(dif_gpio_read(&gpio_, 0, nullptr));
EXPECT_DIF_BADARG(dif_gpio_read(nullptr, 0, nullptr));
EXPECT_DIF_BADARG(dif_gpio_read(&gpio_, kDifGpioNumPins, &out_arg_bool));
}
TEST_F(ReadTest, AllPins) {
constexpr uint32_t kVal = 0xA5A5A5A5;
EXPECT_READ32(GPIO_DATA_IN_REG_OFFSET, kVal);
dif_gpio_state_t pin_values = 0;
EXPECT_DIF_OK(dif_gpio_read_all(&gpio_, &pin_values));
EXPECT_EQ(pin_values, kVal);
}
TEST_F(ReadTest, SinglePin) {
for (uint32_t pin = 0; pin < 32; ++pin) {
for (const bool exp_pin_val : {true, false}) {
const uint32_t reg_val =
exp_pin_val ? AllZerosExcept(pin) : AllOnesExcept(pin);
EXPECT_READ32(GPIO_DATA_IN_REG_OFFSET, reg_val);
bool pin_val = !exp_pin_val;
EXPECT_DIF_OK(dif_gpio_read(&gpio_, pin, &pin_val));
EXPECT_EQ(pin_val, exp_pin_val);
}
}
}
// Write tests
class WriteTest : public GpioTestInitialized {};
TEST_F(WriteTest, BadArgs) {
EXPECT_DIF_BADARG(dif_gpio_write_all(nullptr, kAllOnes));
EXPECT_DIF_BADARG(dif_gpio_write(nullptr, 0, true));
EXPECT_DIF_BADARG(dif_gpio_write_masked(nullptr, kAllOnes, kAllOnes));
EXPECT_DIF_BADARG(dif_gpio_write(&gpio_, kDifGpioNumPins, true));
}
TEST_F(WriteTest, AllPins) {
constexpr uint32_t kVal = 0xA5A5A5A5;
EXPECT_WRITE32(GPIO_DIRECT_OUT_REG_OFFSET, kVal);
EXPECT_DIF_OK(dif_gpio_write_all(&gpio_, kVal));
}
// The GPIO device provides masked bit-level atomic writes to its DIRECT_OUT
// and DIRECT_OE registers. A 32-bit mask and a 32-bit value
//
// mask = [mask_upper, mask_lower],
// bits: 31......16 15.......0
//
// val = [val_upper, val_lower]
// bits: 31.....16 15......0
//
// are written to the registers such that
//
// MASKED_*_UPPER = [mask_upper, val_upper]
// bits: 31......16 15......0
//
// MASKED_*_LOWER = [mask_lower, val_lower]
// bits: 31......16 15......0
//
// Functions that operate on individual pins basically work in the same way
// after creating the equivalent mask and value.
TEST_F(WriteTest, SinglePin) {
EXPECT_WRITE32(GPIO_MASKED_OUT_LOWER_REG_OFFSET, {{16, 1}, {0, 1}});
EXPECT_DIF_OK(dif_gpio_write(&gpio_, 0, true));
EXPECT_WRITE32(GPIO_MASKED_OUT_LOWER_REG_OFFSET, {{31, 1}, {15, 0}});
EXPECT_DIF_OK(dif_gpio_write(&gpio_, 15, false));
EXPECT_WRITE32(GPIO_MASKED_OUT_UPPER_REG_OFFSET, {{16, 1}, {0, 1}});
EXPECT_DIF_OK(dif_gpio_write(&gpio_, 16, true));
EXPECT_WRITE32(GPIO_MASKED_OUT_UPPER_REG_OFFSET, {{31, 1}, {15, 0}});
EXPECT_DIF_OK(dif_gpio_write(&gpio_, 31, false));
}
TEST_F(WriteTest, Masked) {
EXPECT_WRITE32(GPIO_MASKED_OUT_LOWER_REG_OFFSET, 0xCDCD3322);
EXPECT_WRITE32(GPIO_MASKED_OUT_UPPER_REG_OFFSET, 0xABAB5544);
EXPECT_DIF_OK(dif_gpio_write_masked(&gpio_, 0xABABCDCD, 0x55443322));
EXPECT_WRITE32(GPIO_MASKED_OUT_UPPER_REG_OFFSET, 0xABAB5544);
EXPECT_DIF_OK(dif_gpio_write_masked(&gpio_, 0xABAB0000, 0x55443322));
EXPECT_WRITE32(GPIO_MASKED_OUT_LOWER_REG_OFFSET, 0xCDCD3322);
EXPECT_DIF_OK(dif_gpio_write_masked(&gpio_, 0x0000CDCD, 0x55443322));
}
// Output mode tests
class OutputModeTest : public GpioTestInitialized {};
TEST_F(OutputModeTest, BadArgs) {
EXPECT_DIF_BADARG(dif_gpio_output_set_enabled_all(nullptr, kAllOnes));
EXPECT_DIF_BADARG(dif_gpio_output_set_enabled(nullptr, 0, kDifToggleEnabled));
EXPECT_DIF_BADARG(
dif_gpio_output_set_enabled(&gpio_, kDifGpioNumPins, kDifToggleEnabled));
EXPECT_DIF_BADARG(
dif_gpio_output_set_enabled_masked(nullptr, kAllOnes, kAllOnes));
}
TEST_F(OutputModeTest, AllPins) {
constexpr uint32_t kVal = 0xA5A5A5A5;
EXPECT_WRITE32(GPIO_DIRECT_OE_REG_OFFSET, kVal);
EXPECT_DIF_OK(dif_gpio_output_set_enabled_all(&gpio_, kVal));
}
TEST_F(OutputModeTest, SinglePin) {
EXPECT_WRITE32(GPIO_MASKED_OE_LOWER_REG_OFFSET, {{16, 1}, {0, 1}});
EXPECT_DIF_OK(dif_gpio_output_set_enabled(&gpio_, 0, kDifToggleEnabled));
EXPECT_WRITE32(GPIO_MASKED_OE_LOWER_REG_OFFSET, {{31, 1}, {15, 0}});
EXPECT_DIF_OK(dif_gpio_output_set_enabled(&gpio_, 15, kDifToggleDisabled));
EXPECT_WRITE32(GPIO_MASKED_OE_UPPER_REG_OFFSET, {{16, 1}, {0, 1}});
EXPECT_DIF_OK(dif_gpio_output_set_enabled(&gpio_, 16, kDifToggleEnabled));
EXPECT_WRITE32(GPIO_MASKED_OE_UPPER_REG_OFFSET, {{31, 1}, {15, 0}});
EXPECT_DIF_OK(dif_gpio_output_set_enabled(&gpio_, 31, kDifToggleDisabled));
}
TEST_F(OutputModeTest, Masked) {
EXPECT_WRITE32(GPIO_MASKED_OE_LOWER_REG_OFFSET, 0xCDCD3322);
EXPECT_WRITE32(GPIO_MASKED_OE_UPPER_REG_OFFSET, 0xABAB5544);
EXPECT_DIF_OK(
dif_gpio_output_set_enabled_masked(&gpio_, 0xABABCDCD, 0x55443322));
EXPECT_WRITE32(GPIO_MASKED_OE_LOWER_REG_OFFSET, 0xCDCD3322);
EXPECT_DIF_OK(
dif_gpio_output_set_enabled_masked(&gpio_, 0x0000CDCD, 0x55443322));
EXPECT_WRITE32(GPIO_MASKED_OE_UPPER_REG_OFFSET, 0xABAB5544);
EXPECT_DIF_OK(
dif_gpio_output_set_enabled_masked(&gpio_, 0xABAB0000, 0x55443322));
}
// Input noise filter tests
class InputFilterTest : public GpioTestInitialized {};
TEST_F(InputFilterTest, BadArgs) {
EXPECT_DIF_BADARG(dif_gpio_input_noise_filter_set_enabled(nullptr, kAllOnes,
kDifToggleEnabled));
EXPECT_DIF_BADARG(dif_gpio_input_noise_filter_set_enabled(
nullptr, kAllOnes, kDifToggleDisabled));
}
TEST_F(InputFilterTest, MaskedEnable) {
constexpr uint32_t kVal = 0xABABABAB;
EXPECT_READ32(GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, 0x0);
EXPECT_WRITE32(GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, kVal);
EXPECT_DIF_OK(
dif_gpio_input_noise_filter_set_enabled(&gpio_, kVal, kDifToggleEnabled));
}
TEST_F(InputFilterTest, MaskedDisable) {
constexpr uint32_t kVal = 0xABABABAB;
EXPECT_READ32(GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, kAllOnes);
EXPECT_WRITE32(GPIO_CTRL_EN_INPUT_FILTER_REG_OFFSET, ~kVal);
EXPECT_DIF_OK(dif_gpio_input_noise_filter_set_enabled(&gpio_, kVal,
kDifToggleDisabled));
}
class IrqTest : public GpioTestInitialized {
protected:
// Expectations for disabling the interrupt triggers of the pins given by
// `pins`.
void ExpectIrqTriggerMaskedDisable(uint32_t pins) {
EXPECT_READ32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, kAllOnes);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, ~pins);
EXPECT_READ32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, kAllOnes);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, ~pins);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, kAllOnes);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, ~pins);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, kAllOnes);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, ~pins);
}
};
TEST_F(IrqTest, MaskedConfigTriggerEdgeRising) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerEdgeRising");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, kVal);
EXPECT_DIF_OK(
dif_gpio_irq_set_trigger(&gpio_, kVal, kDifGpioIrqTriggerEdgeRising));
}
TEST_F(IrqTest, MaskedConfigTriggerEdgeFalling) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerEdgeFalling");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, kVal);
EXPECT_DIF_OK(
dif_gpio_irq_set_trigger(&gpio_, kVal, kDifGpioIrqTriggerEdgeFalling));
}
TEST_F(IrqTest, MaskedConfigTriggerLevelLow) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerLevelLow");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, kVal);
EXPECT_DIF_OK(
dif_gpio_irq_set_trigger(&gpio_, kVal, kDifGpioIrqTriggerLevelLow));
}
TEST_F(IrqTest, MaskedConfigTriggerLevelHigh) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerLevelHigh");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, kVal);
EXPECT_DIF_OK(
dif_gpio_irq_set_trigger(&gpio_, kVal, kDifGpioIrqTriggerLevelHigh));
}
TEST_F(IrqTest, MaskedConfigTriggerEdgeRisingFalling) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerEdgeRisingFalling");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, kVal);
EXPECT_DIF_OK(dif_gpio_irq_set_trigger(&gpio_, kVal,
kDifGpioIrqTriggerEdgeRisingFalling));
}
TEST_F(IrqTest, MaskedConfigTriggerEdgeRisingLevelLow) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerEdgeRisingLevelLow");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_RISING_REG_OFFSET, kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLLOW_REG_OFFSET, kVal);
EXPECT_DIF_OK(dif_gpio_irq_set_trigger(&gpio_, kVal,
kDifGpioIrqTriggerEdgeRisingLevelLow));
}
TEST_F(IrqTest, MaskedConfigTriggerEdgeFallingLevelHigh) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerEdgeFallingLevelHigh");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_FALLING_REG_OFFSET, kVal);
EXPECT_READ32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, 0);
EXPECT_WRITE32(GPIO_INTR_CTRL_EN_LVLHIGH_REG_OFFSET, kVal);
EXPECT_DIF_OK(dif_gpio_irq_set_trigger(
&gpio_, kVal, kDifGpioIrqTriggerEdgeFallingLevelHigh));
}
TEST_F(IrqTest, MaskedConfigTriggerGeneralError) {
SCOPED_TRACE("IrqTest.MaskedConfigTriggerGeneralError");
constexpr uint32_t kVal = 0xABABABAB;
ExpectIrqTriggerMaskedDisable(kVal);
EXPECT_EQ(dif_gpio_irq_set_trigger(
&gpio_, kVal, static_cast<dif_gpio_irq_trigger_t>(kAllOnes)),
kDifError);
}
} // namespace
} // namespace dif_gpio_unittest