| /* |
| * Copyright 2023 Google LLC |
| * Copyright lowRISC contributors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| #include "hw/top_matcha/sw/autogen/top_matcha.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/dif/dif_gpio.h" |
| #include "sw/device/lib/dif/dif_pinmux.h" |
| #include "sw/device/lib/dif/dif_pwrmgr.h" |
| #include "sw/device/lib/dif/dif_rv_plic.h" |
| #include "sw/device/lib/runtime/irq.h" |
| #include "sw/device/lib/runtime/log.h" |
| #include "sw/device/lib/testing/autogen/isr_testutils.h" |
| #include "sw/device/lib/testing/pwrmgr_testutils.h" |
| #include "sw/device/lib/testing/rand_testutils.h" |
| #include "sw/device/lib/testing/rv_plic_testutils.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" |
| |
| OTTF_DEFINE_TEST_CONFIG(); |
| |
| // PLIC structures |
| static const uint32_t kPlicTarget = kTopMatchaPlicTargetIbex0; |
| static dif_gpio_t gpio; |
| static dif_pwrmgr_t pwrmgr; |
| static dif_pinmux_t pinmux; |
| static dif_pwrmgr_domain_config_t pwrmgr_domain_cfg; |
| static dif_rv_plic_t plic; |
| |
| static plic_isr_ctx_t plic_ctx = {.rv_plic = &plic, .hart_id = kPlicTarget}; |
| static pwrmgr_isr_ctx_t pwrmgr_isr_ctx = { |
| .pwrmgr = &pwrmgr, |
| .plic_pwrmgr_start_irq_id = kTopMatchaPlicIrqIdPwrmgrAonWakeup, |
| .expected_irq = kDifPwrmgrIrqWakeup, |
| .is_only_irq = true}; |
| |
| // SV randomizes the round of entering/exiting sleep then set this volatile |
| // variable via backdoor_overwrite. |
| static volatile const uint8_t kRounds = 2; |
| |
| // To wakeup and maintain GPIO, for now test enters to normal sleep only. |
| static const bool deepPowerdown = false; |
| |
| // Num of GPIO Pads to test |
| enum { kNumGpioPads = 8 }; |
| |
| /** |
| * External interrupt handler. |
| */ |
| void ottf_external_isr(void) { |
| dif_pwrmgr_irq_t irq_id; |
| top_matcha_plic_peripheral_t peripheral; |
| |
| isr_testutils_pwrmgr_isr(plic_ctx, pwrmgr_isr_ctx, &peripheral, &irq_id); |
| |
| // Check that both the peripheral and the irq id is correct |
| CHECK(peripheral == kTopMatchaPlicPeripheralPwrmgrAon, |
| "IRQ peripheral: %d is incorrect", peripheral); |
| CHECK(irq_id == kDifPwrmgrIrqWakeup, "IRQ ID: %d is incorrect", irq_id); |
| |
| // Clear PINMUX WKUP_CAUSE reg |
| CHECK_DIF_OK(dif_pinmux_wakeup_cause_clear(&pinmux)); |
| } |
| |
| /** |
| * A round of GPIO[7:0] retention value test. |
| * |
| * The test sequence is: |
| * |
| * 1. Randomly choose GPIO[7:0] value using rand_testutils. |
| * 2. Drive GPIO with the chosen value. |
| * 3. Send the chosen values to SV via LOG_INFO. |
| * 4. Configure PINMUX Retention value opposit to the chosen value for |
| * GPIO[7:0]. |
| * 5. Initiate sleep mode (assuming pinmux pin wake up has been configured.) |
| * 6. WFI() |
| * 7. At this point, chip has been waken up by DV. Send a log to DV that chip |
| * has waken up. |
| * |
| * DV env checks all PIN value. SW simply drives the GPIO and invert the value |
| * for retention. |
| */ |
| void gpio_test(dif_pwrmgr_t *pwrmgr, dif_pinmux_t *pinmux, dif_gpio_t *gpio, |
| int round) { |
| uint8_t gpio_val = 0; |
| dif_pinmux_pad_kind_t pad_kind; |
| dif_pinmux_sleep_mode_t pad_mode; |
| |
| LOG_INFO("Current Test Round: %1d", round); |
| |
| // 1. Randomly choose GPIO value |
| gpio_val = rand_testutils_gen32_range(0, 255); |
| |
| // 2. Drive GPIO with the chosen value. |
| CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)0x000000FF, |
| (dif_gpio_state_t)gpio_val)); |
| |
| // 3. Send the chosen value to SV via LOG_INFO. |
| // |
| // The format is: |
| // |
| // Chosen GPIO value: %2x |
| LOG_INFO("Chosen GPIO value: %2x", gpio_val); |
| |
| // 4. Configure PINMUX Retention value opposite to the chosen value. |
| pad_kind = kDifPinmuxPadKindMio; |
| for (int i = 0; i < kNumGpioPads; i++) { |
| // GPIO are assigned starting from MIO0 |
| pad_mode = ((gpio_val >> i) & 0x1) ? kDifPinmuxSleepModeLow |
| : kDifPinmuxSleepModeHigh; |
| CHECK_DIF_OK(dif_pinmux_pad_sleep_enable( |
| pinmux, kTopMatchaPinmuxMioOutIoa0 + i, pad_kind, pad_mode)); |
| } |
| |
| // 5. Initiate sleep mode |
| pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceThree, |
| pwrmgr_domain_cfg); |
| // 6. WFI() |
| LOG_INFO("Entering low power mode."); |
| wait_for_interrupt(); |
| |
| // 7. Turn-off retention. |
| for (int i = 0; i < kNumGpioPads; i++) { |
| CHECK_DIF_OK(dif_pinmux_pad_sleep_clear_state( |
| pinmux, kTopMatchaPinmuxMioOutIoa0 + i, pad_kind)); |
| } |
| } |
| |
| /** |
| * Configure GPIO |
| * |
| * gpio_init() configures first 8 MIO PADs to GPIO[7:0]. |
| */ |
| void gpio_init(const dif_pinmux_t *pinmux, const dif_gpio_t *gpio) { |
| // Drive GPIO first |
| CHECK_DIF_OK( |
| dif_gpio_output_set_enabled_all(gpio, (dif_gpio_state_t)0x000000FFu)); |
| CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)0x000000FF, |
| (dif_gpio_state_t)0x00000000)); |
| |
| // Configure PINMUX to GPIO |
| for (int i = 0; i < kNumGpioPads; i++) { |
| CHECK_DIF_OK(dif_pinmux_input_select( |
| pinmux, kTopMatchaPinmuxPeripheralInGpioGpio0 + i, |
| kTopMatchaPinmuxInselIoa0 + i)); |
| CHECK_DIF_OK(dif_pinmux_output_select(pinmux, |
| kTopMatchaPinmuxMioOutIoa0 + i, |
| kTopMatchaPinmuxOutselGpioGpio0 + i)); |
| } |
| } |
| |
| bool test_main(void) { |
| bool result = true; |
| |
| dif_pinmux_wakeup_config_t wakeup_cfg; |
| |
| // Default Deep Power Down |
| |
| // Enable global and external IRQ at Ibex. |
| irq_global_ctrl(true); |
| irq_external_ctrl(true); |
| |
| // Initialize power manager |
| CHECK_DIF_OK(dif_pwrmgr_init( |
| mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); |
| CHECK_DIF_OK(dif_rv_plic_init( |
| mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); |
| CHECK_DIF_OK(dif_pinmux_init( |
| mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); |
| CHECK_DIF_OK( |
| dif_gpio_init(mmio_region_from_addr(TOP_MATCHA_GPIO_BASE_ADDR), &gpio)); |
| |
| // Enable all the AON interrupts used in this test. |
| rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, |
| kTopMatchaPlicIrqIdPwrmgrAonWakeup, |
| kTopMatchaPlicIrqIdPwrmgrAonWakeup); |
| // Enable pwrmgr interrupt |
| CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled)); |
| |
| // Wakeup configs |
| wakeup_cfg.mode = kDifPinmuxWakeupModePositiveEdge; |
| wakeup_cfg.signal_filter = false; |
| wakeup_cfg.pad_type = 0; // MIO |
| wakeup_cfg.pad_select = kTopMatchaPinmuxInselIoa8; // MIO08 |
| |
| // Configure Wakeup Detector 0 |
| CHECK_DIF_OK(dif_pinmux_wakeup_detector_enable(&pinmux, 0, wakeup_cfg)); |
| |
| if (deepPowerdown == false) { |
| // Configure Normal Sleep |
| pwrmgr_domain_cfg = kDifPwrmgrDomainOptionMainPowerInLowPower | |
| kDifPwrmgrDomainOptionUsbClockInActivePower; |
| } |
| |
| LOG_INFO("Num Rounds: %3d", kRounds); |
| |
| // Select IOA0:IOA7 to GPIO |
| gpio_init(&pinmux, &gpio); |
| |
| // Set wakeup condition. Always use GPIO[8] for Pinmux PIN Wakeup. |
| |
| for (int i = kRounds - 1; i >= 0; i--) { |
| gpio_test(&pwrmgr, &pinmux, &gpio, i); |
| } |
| |
| return result; |
| } |