Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: Add pm.wakeup_external_interrupt test. #81522

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions tests/subsys/pm/wakeup_external_interrupt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
**TESTS**

**pm.wakeup_external_interrupt:** Verify the system can wake up from sleep modes using an external interrupt.
steps:
```
Zephyr App:
-> Setup Wakeup Source (PF4 (INT4))
-> Go sleep
-> Waiting for External interrupt

Pytest:
-> Init Power Monitor
-> Start measure
-> Connect to the Controller's shell
-> Init PC6 as output (Controller side)
-> Set GPIO High to wake up the Target (Controller side)
-> Stop Measure
-> Analyze data
```

**SETUP**
![image](https://github.com/user-attachments/assets/493adb86-cf78-4577-a10e-5f3b24c6fef7)

![image](https://github.com/user-attachments/assets/2627f4ce-81a2-4e61-8a58-9deeaf6aa6d9)

**To Reproduce**
1. Configure the boards according to above diagram.
2. Connect all 3 usb to the host.
3. Copy the path of the `Power Monitor`, `Controller` and update the `<path_to_power_monitor>`,`<power_to_controller>` in the `tests/subsys/pm/wakeup_external_interrupt/target/testcase.yaml` file
under the pytest_args section as follows:

`pytest_args: [--power-monitor=<path_to_power_monitor>,--controller=<path_to_controller>]`

4. Run the test `pm.wakeup_external_interrupt.controller` to build and flash application to the controller.
```
twister --device-testing --device-serial <path_to_controller> -p stm32l562e_dk -s pm.wakeup_external_interrupt.controller --west-flash="--dev-id=<controller_serial_number>" --west-runner=pyocd
```

To get the `<controller_serial_number>` run:
`pyocd list`
Output:
```
# Probe/Board Unique ID Target
-------------------------------------------------------------------
0 STLINK-V3 0048004##########3333639 ✔︎ stm32l562qeixq
STM32L562E-DK

1 STLINK-V3 004E003##########3333639 ✔︎ stm32l562qeixq
STM32L562E-DK
```

5. Run the test `pm.wakeup_external_interrupt.target`.

```
twister --device-testing --device-serial <path_to_controller> -p stm32l562e_dk -s pm.wakeup_external_interrupt.target --west-flash="--dev-id=<controller_serial_number>" --west-runner=pyocd
```

Twister log:
```
DEBUG - PYTEST: ============================= test session starts ==============================
DEBUG - PYTEST: platform linux -- Python 3.10.12, pytest-8.1.0, pluggy-1.4.0 -- /usr/bin/python3
DEBUG - PYTEST: cachedir: .pytest_cache
DEBUG - PYTEST: rootdir: <path_to>/zephyr
DEBUG - PYTEST: collecting ... collected 1 item
DEBUG - PYTEST: tests/subsys/pm/wakeup_external_interrupt/target/pytest/test_wakeup_external_interrupt.py::test_wakeup_external_interrupt
DEBUG - PYTEST: -------------------------------- live log setup --------------------------------
DEBUG - PYTEST: DEBUG: Get device type "hardware"
DEBUG - PYTEST: DEBUG: Opening serial connection for /dev/ttyACM1
DEBUG - PYTEST: DEBUG: Flashing command: <path_to>/.local/bin/west flash --skip-rebuild --build-dir <path_to>/zephyr/twister-out/stm32l562e_dk/tests/subsys/pm/wakeup_external_interrupt/target/pm.wakeup_external_interrupt.target --runner pyocd -- --dev-id=0048004############33639
DEBUG - PYTEST: DEBUG: Flashing finished
DEBUG - PYTEST: [CMD] htc
DEBUG - PYTEST: PowerShield > ack htc
DEBUG - PYTEST: [CMD] format bin_hexa
DEBUG - PYTEST: PowerShield > ack format bin_hexa
DEBUG - PYTEST: [CMD] freq 1k
DEBUG - PYTEST: PowerShield > ack freq 1k
DEBUG - PYTEST: [CMD] acqtime 0
DEBUG - PYTEST: PowerShield > ack acqtime 0
DEBUG - PYTEST: [CMD] volt 3300m
DEBUG - PYTEST: PowerShield > ack volt 3300m
DEBUG - PYTEST: [CMD] funcmode high
DEBUG - PYTEST: PowerShield > ack funcmode high
DEBUG - PYTEST: [CMD] start
DEBUG - PYTEST: setting gpioc 6 as output
DEBUG - PYTEST: setting gpioc 6 to 1
DEBUG - PYTEST: setting gpioc 6 to 0
DEBUG - PYTEST: [CMD] stop
DEBUG - PYTEST: stop 2:
DEBUG - PYTEST: Expected RMS: 0.34 mA
DEBUG - PYTEST: Tolerance: 0.03 mA
DEBUG - PYTEST: Current RMS: 0.31 mA
DEBUG - PYTEST: active:
DEBUG - PYTEST: Expected RMS: 95.00 mA
DEBUG - PYTEST: Tolerance: 9.50 mA
DEBUG - PYTEST: Current RMS: 95.32 mA
DEBUG - PYTEST: PASSED
DEBUG - PYTEST: ------------------------------ live log teardown -------------------------------
DEBUG - PYTEST: DEBUG: Closed serial connection for /dev/ttyACM1
DEBUG - PYTEST: - generated xml file: <path_to>twister-out/stm32l562e_dk/tests/subsys/pm/wakeup_external_interrupt/target/pm.wakeup_external_interrupt.target/report.xml -
DEBUG - PYTEST: ============================== 1 passed in 15.35s ==============================
DEBUG - run status: stm32l562e_dk/tests/subsys/pm/wakeup_external_interrupt/target/pm.wakeup_external_interrupt.target passed
INFO - 1/1 stm32l562e_dk tests/subsys/pm/wakeup_external_interrupt/target/pm.wakeup_external_interrupt.target PASSED (device 15.371s)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wakeup_external_controller)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
5 changes: 5 additions & 0 deletions tests/subsys/pm/wakeup_external_interrupt/controller/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_SHELL=y
CONFIG_DEVICE_SHELL=y
CONFIG_GPIO=y
CONFIG_GPIO_SHELL=y
CONFIG_GPIO_SHELL_INFO_CMD=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025 Intel Corporation.
# SPDX-License-Identifier: Apache-2.0

from twister_harness import Shell


def test_shell_print_version(shell: Shell):
lines = shell.exec_command("kernel version")
assert any(['Zephyr version' in line for line in lines]), 'failed to retrieve Zephyr version'
11 changes: 11 additions & 0 deletions tests/subsys/pm/wakeup_external_interrupt/controller/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2025 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>

int main(void)
{
return 0;
}
10 changes: 10 additions & 0 deletions tests/subsys/pm/wakeup_external_interrupt/controller/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests:
pm.wakeup_external_interrupt.controller:
platform_allow:
- stm32l562e_dk
tags:
- shell
- gpio
harness: pytest
harness_config:
pytest_dut_scope: session
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wakeup_external_target_board)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/ {
aliases {
gwu0 = &wakeup_pin_0;
};
gpiof {
wakeup-source;
};
gpio_keys {
wakeup_pin_0: wakeup_pin_0 {
gpios = < &gpiof 0x4 GPIO_ACTIVE_HIGH >;
label = "GPIO wake-up pin 0";
};
};
};

&stop0{
compatible = "zephyr,power-state";
power-state-name = "suspend-to-idle";
substate-id = <1>;
min-residency-us = <500000>;
};
&stop1{
compatible = "zephyr,power-state";
power-state-name = "suspend-to-idle";
substate-id = <2>;
min-residency-us = <1000000>;
};
&stop2{
compatible = "zephyr,power-state";
power-state-name = "suspend-to-idle";
substate-id = <3>;
min-residency-us = <1500000>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
power_states:
k_cpu_idle:
rms: 56.0
state_0:
rms: 4.0
state_1:
rms: 1.3
state_2:
rms: 0.27
active:
rms: 94
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_GPIO=y
CONFIG_PM=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
#
# Copyright (c) 2025 Intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
import threading
import time

import pytest
import serial
import yaml
from twister_harness import DeviceAdapter

from scripts.pm.power_monitor_stm32l562e_dk.PowerShield import PowerShield


def pytest_addoption(parser):
parser.addoption("--controller-device", help="Path to controller device.")
parser.addoption("--powershield", help="Path to PowerShield device")
parser.addoption("--expected-values", help="Path to yaml file with expected values")


def load_power_state_data(path):
with open(path) as file:
return yaml.safe_load(file)


@pytest.fixture
def expected_values(request):
expected_rms_file = request.config.getoption("--expected-values")
return load_power_state_data(expected_rms_file)['power_states']


def send_shell_commands(controllerDevice):
controller = serial.Serial(
controllerDevice,
baudrate=115200,
bytesize=serial.EIGHTBITS,
stopbits=serial.STOPBITS_ONE,
parity=serial.PARITY_NONE,
timeout=1,
)
time.sleep(13)
print("Setting gpioc 6 as output")
controller.write(b"gpio conf gpioc 6 o\n")
print("Setting gpioc 6 to 1")
controller.write(b"gpio set gpioc 6 1\n")
time.sleep(1)
controller.write(b"gpio set gpioc 6 0\n")
print("Setting gpioc 6 to 0")


@pytest.fixture(scope="module")
def power_monitor_measure(dut: DeviceAdapter, request):
powershield_device = request.config.getoption("--powershield")
controller_device = request.config.getoption("--controller-device")
shell_thread = threading.Thread(target=send_shell_commands, args=(controller_device,))
shell_thread.start()
PM_Device = PowerShield()
PM_Device.init(power_device_path=powershield_device)
PM_Device.measure(time=6)
data = PM_Device.get_data()
return data
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (c) 2025 Intel Corporation.
# SPDX-License-Identifier: Apache-2.0

from scripts.pm.power_monitor_stm32l562e_dk.UnityFunctions import UnityFunctions


def test_wakeup_timer(current_measurement_output, expected_values):
data = current_measurement_output
pm_states_expected_values = expected_values.items()

value_in_amps = UnityFunctions.current_RMS(data, exclude_first_600=True, num_peaks=1)
value_in_amps = value_in_amps[0:2]
# Convert to milliamps
value_in_miliamps = [value * 1e4 for value in value_in_amps]

def print_state_rms(state_label, rms_expected_value, rms_measured_value):
tolerance = rms_expected_value / 10

print(f"{state_label}:")
print(f"Expected RMS: {rms_expected_value:.2f} mA")
print(f"Tolerance: {tolerance:.2f} mA")
print(f"Current RMS: {rms_measured_value:.2f} mA")

assert (
(rms_expected_value - tolerance) < rms_measured_value < (rms_expected_value + tolerance)
), f"RMS value out of tolerance for {state_label}"

# Define the state labels
state_labels = ["state_2", "active"]

# Convert dict_items to dictionary if needed
pm_states_expected_values = dict(pm_states_expected_values)
# Now you can use indexing or key-based access
for state_label, rms_value in zip(state_labels, value_in_miliamps, strict=False):
rms_expected_value = pm_states_expected_values[state_label]['rms']
print_state_rms(state_label, rms_expected_value, rms_value)
84 changes: 84 additions & 0 deletions tests/subsys/pm/wakeup_external_interrupt/target_board/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2025 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>

#define GWU0_NODE DT_ALIAS(gwu0)
#if !DT_NODE_HAS_STATUS(GWU0_NODE, okay)
#error "Error: gwu0 devicetree alias is not defined"
#endif

static const struct gpio_dt_spec wakeupPin = GPIO_DT_SPEC_GET(GWU0_NODE, gpios);
static struct gpio_callback wakeupPin_cb_data;

volatile k_tid_t my_tid;

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5
struct k_thread my_thread_data;
K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);

void my_entry_point(void *, void *, void *)
{
printk("Going sleep.\n");
k_msleep(10000);
}

void wakeup_cb(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
printk("Rising edge detected\n");
k_thread_abort(my_tid);
}

int main(void)
{
int ret;

if (!gpio_is_ready_dt(&wakeupPin)) {
printk("Error: wake-up gpio device %s is not ready\n",
wakeupPin.port->name);
return 0;
}

ret = gpio_pin_configure_dt(&wakeupPin, GPIO_INPUT | GPIO_PULL_DOWN);
if (ret != 0) {
printk("Error %d: failed to configure %s pin %d\n",
ret, wakeupPin.port->name, wakeupPin.pin);
return 0;
}

ret = gpio_pin_interrupt_configure_dt(&wakeupPin,
GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n",
ret, wakeupPin.port->name, wakeupPin.pin);
return 0;
}

gpio_init_callback(&wakeupPin_cb_data, wakeup_cb, BIT(wakeupPin.pin));
gpio_add_callback(wakeupPin.port, &wakeupPin_cb_data);
printk("Wake-up set at %s pin %d\n", wakeupPin.port->name, wakeupPin.pin);

printk("Created the thread.\n");
my_tid = k_thread_create(
&my_thread_data, my_stack_area,
K_THREAD_STACK_SIZEOF(my_stack_area),
my_entry_point,
NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);

k_thread_join(my_tid, K_FOREVER);

while (1) {
arch_nop();
}
return 0;
}
Loading
Loading