zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.96k stars 6.67k forks source link

RFC: New Testing Approach for Power Management Subsystem. #80989

Open gbarkadiusz opened 3 weeks ago

gbarkadiusz commented 3 weeks ago

Introduction

This proposal suggests a new method for testing the Power Management (PM) subsystem in Zephyr. The approach enhances the testing environment by adding an external power monitor to verify that the system reaches expected power states accurately. This improvement aims to make power management tests more reliable and to provide precise measurements for validating power state transitions.

Problem description

The Zephyr Power Management subsystem provides multiple sleep modes for efficient power usage. However, reliably confirming that a device enters each specific power state is challenging. For example, the stm32l562e_dk board supports multiple power states (k_cpu_idle, stop_0, stop_1, stop_2), but current test methods are limited in confirming that each state is reached as expected.

Proposed change

This proposal involves integrating a Power Monitor to track current consumption during testing. Using the stm32l562e_dk board’s onboard Power Monitor, the test setup can measure current in each power state. Pytest will then calculate the Root Mean Square (RMS) value of each detected power state, making it easier to verify whether each state has been achieved. This allows for accurate state detection and validation based on measured current consumption.

Detailed RFC

An external Power Monitor will be added to measure the current of the target device during tests. The stm32l562e_dk board, which has an onboard Power Monitor, will be used for testing. A script named pwsh_stm32l562.py will facilitate communication with the board, handling the data retrieval and current measurement.

Proposed change (Detailed)

The Power_Monitor abstract class serves as a base for implementing this functionality. It requires the following methods to be implemented:

init_power_monitor(device_id: str) -> bool: Initializes the power monitor for a specific device ID. measure_current(duration: int): Measures current over a given duration. get_data(duration: int) -> List[float]: Retrieves an array of current measurements over the specified duration, returning values in amperes.

Setup and Configuration image Documentation: STM32L562E-DK Evaluation Board: STMicroelectronics STM32L562E-DK STM32L562E-DK User Manual: User Manual DM0063555 Power Monitor Firmware Documentation: Getting Started with PowerShield Firmware Configuration: Based on the documentation provided, the stm32l562e_dk board should be configured as follows: image

Three tests demonstrate this approach, each designed to validate different aspects of power management:

pm.states: Verifies the transition to all available power states on stm32l562e_dk. _pm.residencytime: Tests the residency time of each power state. _pm.wakeuptimer: Tests the RTC alarm’s ability to wake the CPU from the lowest power state. Each test calculates RMS current values for each detected state and compares them to expected values, which were manually set based on experimental data.

Test: pm.states This test is designed to verify all available power states on the stm32l562e_dk board. The results from the measure_current function can be found in the pm_states.csv file. image The chart above illustrates the measured current consumption during the execution of the pm_states test. Key elements of the chart are highlighted as follows:

Startup: The current peak observed at the beginning of the test. Boot Delay: The delay associated with the additional configuration set to CONFIG_BOOT_DELAY=500. _k_cpuidle: The low power state achieved using k_msleep(Tms), where Tms is shorter than the minimum residency time for the stop_0 power state defined in the device tree source (.dts) file. Wake Up and Print Log: The target board wakes up between each power state and logs the corresponding messages. _stop_0, stop_1, stop_2, activestate: The measured current consumption for each respective power state.

During the test, pytest detects each step and calculates the RMS (Root Mean Square) current for every detected state. The test then compares the calculated RMS values against the expected values, which have been determined manually through experimental observation

To Reproduce:

  1. Configure the stm32l562e_dk board according to the instructions provided above.
  2. Connect both USB ports of the board to your host system. The board will appear as two separate targets:
    • Power Monitor: e.g., /dev/ttyACM0 or /dev/serial/by-id/usb-STMicroelectronics_PowerShield__Virtual_ComPort_in_FS_Mode__FFFFFFFEFFFF-if00
    • Target: e.g., /dev/ttyACM1 or /dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004##############39-if02
  3. Copy the path of the Power Monitor and update the <path_to_power_monitor> in the tests/subsys/pm/power_states/testcase.yaml file under the pytest_args section as follows:
    pytest_args: [--powershield=<path_to_power_monitor>]
  4. Run the following command to execute the test:
    twister --device-testing --device-serial <path_to_target> -p stm32l562e_dk -T tests/subsys/pm/power_states/ -x=CONFIG_BOOT_DELAY=500 -vv

    Pytest log:

    DEBUG   - PYTEST: k_cpu_idle:
    DEBUG   - PYTEST: Expected RMS: 57.00 mA
    DEBUG   - PYTEST: Tolerance: 5.70 mA
    DEBUG   - PYTEST: Current RMS: 56.44 mA
    DEBUG   - PYTEST: state 0:
    DEBUG   - PYTEST: Expected RMS: 4.00 mA
    DEBUG   - PYTEST: Tolerance: 0.40 mA
    DEBUG   - PYTEST: Current RMS: 3.99 mA
    DEBUG   - PYTEST: state 1:
    DEBUG   - PYTEST: Expected RMS: 1.30 mA
    DEBUG   - PYTEST: Tolerance: 0.13 mA
    DEBUG   - PYTEST: Current RMS: 1.29 mA
    DEBUG   - PYTEST: state 2:
    DEBUG   - PYTEST: Expected RMS: 0.27 mA
    DEBUG   - PYTEST: Tolerance: 0.03 mA
    DEBUG   - PYTEST: Current RMS: 0.27 mA
    DEBUG   - PYTEST: PASSED

Test: pm.residency_time This test evaluates the residency time of different power states on the stm32l562e_dk board. The sequence of states is as follows: image The chart above illustrates the measured current consumption during the execution of the pm_residency_time test. Key elements of the chart are highlighted as follows: Startup: The current peak observed at the beginning of the test. Boot Delay: The delay associated with the additional configuration set to CONFIG_BOOT_DELAY=500. _k_cpuidle: This represents the kernel idle state, where the sleep time is shorter than the residency time required for the stop_0 state. _stop0: In this state, there is sufficient sleep time for the system to enter stop_0. _A_stop1: Here, the sleep time is again shorter than the residency time required to transition to the stop_1 state. _B_stop1: This state allows for enough sleep time to transition into stop_1. _A_stop2: In this iteration, the sleep time is shorter than the residency time required to transition to the stop_2 state. _B_stop2: The system has enough sleep time to enter the stop_2 state. _activestate: This state indicates that the application is running in an infinite loop, representing the active state of the system. Wake Up and Print Log: The target board wakes up between each power state and logs the corresponding messages.

To Reproduce: (1-2) As previously.

  1. Copy the path of the Power Monitor and update the <path_to_power_monitor> in the tests/subsys/pm/power_residency_time/testcase.yaml file under the pytest_args section as follows:
    pytest_args: [--powershield=<path_to_power_monitor>]
  2. Run the following command to execute the tests:
    twister --device-testing --device-serial <path_to>/Target -p stm32l562e_dk -T tests/subsys/pm/power_residency_time/ -x=CONFIG_BOOT_DELAY=500 -vv

    Pytest Log:

    DEBUG   - PYTEST: k_cpu_idle:
    DEBUG   - PYTEST: Expected RMS: 57.00 mA
    DEBUG   - PYTEST: Tolerance: 5.70 mA
    DEBUG   - PYTEST: Current RMS: 56.37 mA
    DEBUG   - PYTEST: state 0:
    DEBUG   - PYTEST: Expected RMS: 4.00 mA
    DEBUG   - PYTEST: Tolerance: 0.40 mA
    DEBUG   - PYTEST: Current RMS: 3.78 mA
    DEBUG   - PYTEST: state 0:
    DEBUG   - PYTEST: Expected RMS: 4.00 mA
    DEBUG   - PYTEST: Tolerance: 0.40 mA
    DEBUG   - PYTEST: Current RMS: 3.92 mA
    DEBUG   - PYTEST: state 1:
    DEBUG   - PYTEST: Expected RMS: 1.20 mA
    DEBUG   - PYTEST: Tolerance: 0.12 mA
    DEBUG   - PYTEST: Current RMS: 1.19 mA
    DEBUG   - PYTEST: state 1:
    DEBUG   - PYTEST: Expected RMS: 1.20 mA
    DEBUG   - PYTEST: Tolerance: 0.12 mA
    DEBUG   - PYTEST: Current RMS: 1.18 mA
    DEBUG   - PYTEST: state 2:
    DEBUG   - PYTEST: Expected RMS: 0.27 mA
    DEBUG   - PYTEST: Tolerance: 0.03 mA
    DEBUG   - PYTEST: Current RMS: 0.27 mA
    DEBUG   - PYTEST: active:
    DEBUG   - PYTEST: Expected RMS: 92.70 mA
    DEBUG   - PYTEST: Tolerance: 9.27 mA
    DEBUG   - PYTEST: Current RMS: 94.33 mA
    DEBUG   - PYTEST: PASSED

Test: pm.wakeup_timer In this test, the stm32l562e_dk target board is configured to set the RTC Alarm for 1 second before entering the lowest power state by calling the k_msleep() function. The sequence of operations is as follows: -The RTC Alarm is set for 1 second. -The board enters a low-power state, effectively minimizing power consumption. -After the alarm triggers, the RTC Alarm callback wakes up the CPU. -The CPU then enters an anti-sleeping loop to prevent returning to sleep mode. -Throughout this process, the power monitor continuously measures the power consumption of the board. This test helps verify the functionality of the wake-up timer and assesses the power consumption during low-power operation. image The chart above illustrates the measured current consumption during the execution of the pm_wakeup_timer test. The following key elements are highlighted in the chart: Startup: The current peak observed at the beginning of the test. Boot Delay: The delay associated with the additional configuration set to CONFIG_BOOT_DELAY=500. Go Sleep: This phase represents when the alarm is set, and the board enters a low-power state by calling the k_msleep() function. Stop 2: The measured current consumption during the stop_2 power state. Alarm Interrupt: This indicates the moment the RTC alarm interrupt wakes the CPU, transitioning into an infinite loop. Active State: This state signifies that the application is running in an infinite loop, representing the active operational state of the system.

To Reproduce: (1-2) As previously.

  1. Copy the path of the Power Monitor and update the <path_to_power_monitor> in the tests/subsys/pm/power_wakeup_timer/testcase.yaml file under the pytest_args section as follows:
    pytest_args: [--powershield=<path_to_power_monitor>]
  2. Run the following command to execute the tests:
    twister --device-testing --device-serial <path_to_target> -p stm32l562e_dk -T tests/subsys/pm/power_wakeup_timer/ -x=CONFIG_BOOT_DELAY=500 -vv

    Pytest_log:

    DEBUG   - PYTEST: Expected RMS: 0.26 mA
    DEBUG   - PYTEST: Tolerance: 0.03 mA
    DEBUG   - PYTEST: Current RMS: 0.27 mA
    DEBUG   - PYTEST: active:
    DEBUG   - PYTEST: Expected RMS: 95.00 mA
    DEBUG   - PYTEST: Tolerance: 9.50 mA
    DEBUG   - PYTEST: Current RMS: 91.90 mA
    DEBUG   - PYTEST: PASSED

All the tests mentioned above are available in the following pull request: https://github.com/zephyrproject-rtos/zephyr/pull/80692. This pull request is currently in draft status and will remain so until the RFC is reviewed and applied.

Dependencies

This change requires access to the stm32l562e_dk board and an external (onboarded) Power Monitor. It may also necessitate modification of test scripts if additional power monitors are used.

nashif commented 2 weeks ago

@erwango @bjarki-andreasen @ceolin please take a look

bjarki-andreasen commented 2 weeks ago

I agree! We need to add power usage testing!

Some notes from my side:

I do have ideas for solutions for this as well but that's for later :)

bjarki-andreasen commented 2 weeks ago

Can't help it, I would tie expected power measurement in unit tests to output from probe like:

device output:

*** Booting Zephyr OS build v4.0.0-rc2 ***
Running TESTSUITE power_test
===================================================================
START - test_power
 PROBE POWER - 100us 1uJ 3uJ
 PROBE POWER - 100us 6uJ 10uJ
 PASS - test_power in 0.200 seconds
===================================================================

test runner listens for PROBE POWER - <duration> <min> <max> and starts measuring power with whatever power measuring probe. The test suite code could look like:

ZTEST(power_test, test_power)
{
        /* enter power mode */
        ...

        /* request power measurement for duration of 100us expecting between 1 and 3 uJ */
        ztest_probe_power(100, 1, 3);

        /* enter next power mode */
        ...

        /* request power measurement for duration of 100us expecting between 6 and 10 uJ */
        ztest_probe_power(100, 6, 10);
}

over UART, ztest_probe_power(100, 1, 3) would result in printing PROBE POWER - 100 1 3 which prompts the test runner to perform the measurement using the probe, and fail the test suite if actual power was not within limits.

This is scalable to probing for other things like temperatures, voltage RMS, etc etc.

*** Booting Zephyr OS build v4.0.0-rc2 ***
Running TESTSUITE power_test
===================================================================
START - test_power
 PROBE CURRENT AVG - 100us 12uA 14uA
 PROBE VOLTAGE RMS - 100us 1.2uV 1.3uV
 PASS - test_power in 0.200 seconds
===================================================================
gbarkadiusz commented 1 week ago

I agree! We need to add power usage testing!

Some notes from my side:

  • We should measure power usage in watts (joules) :) amps only tell half the story
  • We should support power monitors as flexibly as we support debuggers in west. I would likely opt for a https://www.joulescope.com/ for testing
  • We should be testing more than power states, power usage of UARTs, I2C, whole applications really, is really important as well :) We should be able to just "enable power measuring" for any test or application at any time :)

I do have ideas for solutions for this as well but that's for later :)

Regarding these pointed things. Watts are a good choice for measurement, but they require measuring the target board's supply voltage, which makes the setup a bit more complex. Additionally, I’m not sure if West is the best tool for handling this functionality. It could lead to potential issues with synchronization between West and Twister. Moreover, analyzing the results through Twister (as the tool responsible for testing) might be problematic—but I’m not certain, just raising the question.

Another question to consider is whether we should focus on measuring the relative changes in power consumption between different power modes/states, or if we should aim to measure the specific values for each application state. For example, should we also measure the power usage of every peripheral available on a specific target?

bjarki-andreasen commented 1 week ago

Regarding these pointed things. Watts are a good choice for measurement, but they require measuring the target board's supply voltage, which makes the setup a bit more complex.

Yeah, with some probes it may not be possible to measure joules, but in these cases, the test suites should still indicate a number of expected jules, and the user should provide a "fixed" voltage for twister to use to calculate joules from the current measurement :)

Additionally, I’m not sure if West is the best tool for handling this functionality. It could lead to potential issues with synchronization between West and Twister. Moreover, analyzing the results through Twister (as the tool responsible for testing) might be problematic—but I’m not certain, just raising the question.

Looking into it, twister is the tool used to manage tests, so it would be the place to add probes, alongside harnesses IMO :)

Another question to consider is whether we should focus on measuring the relative changes in power consumption between different power modes/states, or if we should aim to measure the specific values for each application state. For example, should we also measure the power usage of every peripheral available on a specific target?

I would strongly encourage specific measurements :) Test suites are about as controlled an environment we can get, changes somewhere which unexpectedly enables/disables some peripheral should be caught as an anomaly.

gbarkadiusz commented 1 week ago

@bjarki-andreasen

I noticed you updated the comment—thank you for that!

I have another question regarding the testing setup. I saw that you're using the ztest framework. Could you please explain how you'd like to integrate the measuring functionality with the ztest framework? I'm having some trouble understanding the connection and would appreciate your guidance.

Thank you!

PS: I can imagine using Serial/Uart to correspond zephyr app with python script to trigger the measurement. e.g.:

void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
    const struct device *uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
    char cmd[64];
    snprintf(cmd, sizeof(cmd), "measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

    // Send the command over UART
    for (size_t i = 0; i < strlen(cmd); ++i) {
        uart_poll_out(uart_dev, cmd[i]);
    }

    // Wait for acknowledgment and result
    char response[128];
    size_t len = 0;
    while (len < sizeof(response) - 1) {
        if (uart_poll_in(uart_dev, &response[len]) == 0) {
            len++;
            if (response[len - 1] == '\n') {
                break; // End of response
            }
        }
    }
    response[len] = '\0';
}
bjarki-andreasen commented 1 week ago

@bjarki-andreasen

I noticed you updated the comment—thank you for that!

I have another question regarding the testing setup. I saw that you're using the ztest framework. Could you please explain how you'd like to integrate the measuring functionality with the ztest framework? I'm having some trouble understanding the connection and would appreciate your guidance.

Thank you!

PS: I can imagine using Serial/Uart to correspond zephyr app with python script to trigger the measurement. e.g.:

void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
    const struct device *uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
    char cmd[64];
    snprintf(cmd, sizeof(cmd), "measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

    // Send the command over UART
    for (size_t i = 0; i < strlen(cmd); ++i) {
        uart_poll_out(uart_dev, cmd[i]);
    }

    // Wait for acknowledgment and result
    char response[128];
    size_t len = 0;
    while (len < sizeof(response) - 1) {
        if (uart_poll_in(uart_dev, &response[len]) == 0) {
            len++;
            if (response[len - 1] == '\n') {
                break; // End of response
            }
        }
    }
    response[len] = '\0';
}

To send back an answer, we would likely need to enable shell support, and add a shell backend for testing, something that would look like:

===================================================================
START - test_power
 PROBE CURRENT AVG - 100us 12uA 14uA
uart:~$ ztest PASS
 PASS - test_power
===================================================================

in the shell, where we would listen for PROBE CURRENT AVG - 100us 12uA 14uA, start the measurement, and send back the status PASS. ztest would be the subcommand, taking one parameter, PASSED or FAILED.

The code could look like:

static K_SEM_DEFINE(shell_result, 0, 1);
static bool passed;

void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
        /* Request measurement */
        TC_PRINT("measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

        /* Wait for shell input */
        k_sem_take(&shell_result, K_FOREVER);

        /* Check result */
        if (!passed) {
                ztest_test_fail();
        }
}

excluding the implementation of the ztest shell backend.

However, test suites currently run with only console enabled, since its simple, and all we need if the entire test suite is run on target. Enabling the shell takes quite a bit of ROM, and may make some targets incompatible with the test suite. For that reason, I would compromise to something like:

void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
        /* Request measurement */
        TC_PRINT("measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

        /* wait "some time" for twister to start test */
        k_sleep(K_MSEC(10));

        /* wait for duration */
        k_sleep(K_USEC(duration_us));
}

Its likely less stable then the shell approach, but its an option :)

The last option would be to have something completely custom similar to your suggestion with directly interacting with the UART, also a fine solution, but we may be limiting ourselves a bit compared to adding shell support

gbarkadiusz commented 6 days ago

I see your point. A Zephyr application can request measurements at any time using methods like TC_PRINT or shell commands. The challenge lies in capturing these requests and performing the measurements. We need a mechanism to handle such commands. As you mentioned, we could use the shell backend (e.g., UART) to send commands, but we still need something on the other end to process these requests. Additionally, I’m considering whether we should use the same serial port for communication with the Zephyr application and for verifying tests via Twister. It could make the Twister log messy.

Thinking out loud: A straightforward approach could be to use the pytest harness. For example:

import pytest
from twister_harness import DeviceAdapter

def test_measure(dut: DeviceAdapter):
    # Read lines from the device's output
    output_lines = dut.readlines()

    # Handle the command from the output
    for line in output_lines:
        if "PROBE POWER" in line:
            # Parse the command, execute measurement logic, and decide pass/fail
            handle_probe_power_command(line, dut)

The handle_probe_power_command function could process the command, execute the measurement, and communicate the result back to the device or log it appropriately. This keeps the process modular and aligns well with pytest's structure.

bjarki-andreasen commented 6 days ago

I see your point. A Zephyr application can request measurements at any time using methods like TC_PRINT or shell commands. The challenge lies in capturing these requests and performing the measurements. We need a mechanism to handle such commands. As you mentioned, we could use the shell backend (e.g., UART) to send commands, but we still need something on the other end to process these requests. Additionally, I’m considering whether we should use the same serial port for communication with the Zephyr application and for verifying tests via Twister. It could make the Twister log messy.

Thinking out loud: A straightforward approach could be to use the pytest harness. For example:

import pytest
from twister_harness import DeviceAdapter

def test_measure(dut: DeviceAdapter):
    # Read lines from the device's output
    output_lines = dut.readlines()

    # Handle the command from the output
    for line in output_lines:
        if "PROBE POWER" in line:
            # Parse the command, execute measurement logic, and decide pass/fail
            handle_probe_power_command(line, dut)

The handle_probe_power_command function could process the command, execute the measurement, and communicate the result back to the device or log it appropriately. This keeps the process modular and aligns well with pytest's structure.

This looks pretty nice :)

gbarkadiusz commented 5 days ago

@bjarki-andreasen I thought about the unit of measured value. We have several options. Pure Average of current. (As you said, it is a half of story). image But the power can be calculated as: image

RMS (Root Mean Square) is a better metric for fluctuating signals as it considers the variation in current over time. RMS current can be calculated as: image and the power can be calculated a: image

Total Energy Instead of Power. If the measurement duration image is known, we can calculate the total energy (𝐸) consumed over the period. This gives a clearer picture of energy usage: image where: image

The question is. How to calculate the expected value? Maybe it is not a good question at this time but let me treat this RFC as a brainstorm and place to share all ideas and warries. :)

bjarki-andreasen commented 5 days ago

I don't think we should limit ourselves at all :) We could add support for power over time, min/max voltages, average current, rms current etc.

ztest_probe_power(); /* in joules */
ztest_probe_current_rms() /* in amps */
ztest_probe_voltage_min_max() /* in volts */

Any test can specify whatever it needs, and will get its way if the probe supports the requested measurement.

gbarkadiusz commented 5 days ago

I don't think we should limit ourselves at all :) We could add support for power over time, min/max voltages, average current, rms current etc.

ztest_probe_power(); /* in joules */
ztest_probe_current_rms() /* in amps */
ztest_probe_voltage_min_max() /* in volts */

Any test can specify whatever it needs, and will get its way if the probe supports the requested measurement.

Sounds great ;)

bjarki-andreasen commented 5 days ago

One little update:

ztest_probe_power_avg(); /* in watts */
ztest_probe_energy(); /* in joules */

power is in watts :)

hakehuang commented 1 day ago

measure with on-board adc is an options, and I from NXP would like to propose to use our mcu-link-pro as a standalone measure device to do this. see https://www.nxp.com/docs/en/user-manual/UM11673.pdf

gbarkadiusz commented 1 day ago

@bjarki-andreasen FYI. I performed some exercises on triggering measurements from the Zephyr application.


void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
    /* Request measurement */
    printk("measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

    /* wait for duration */
    k_sleep(K_MSEC(duration_us));
}

ZTEST(testsuite, test_name_of_testcase)
{
    ztest_probe_power(2000000, 1, 3);
}

ZTEST_SUITE(testsuite, NULL, NULL, NULL, NULL, NULL);

pytest:


def test_foo(dut: DeviceAdapter, request):
    powershield_device = request.config.getoption("--powermonitor")
    PM_Device = PowerMonitor()
    PM_Device.setup(power_device_path = powershield_device)
    while True:
        lines = dut.readlines() 
        # Find the line containing 'measure_power'
        measure_power_line = next((line for line in lines if 'measure_power' in line), None)
        if measure_power_line:
            parts = measure_power_line.split()
            # Extract the values
            command = parts[0]  # "measure_power"
            duration = int(parts[1]) /1000000 # us
            print(duration)
            value2 = int(parts[2])  # 
            value3 = int(parts[3])  # 

            PM_Device.measure(measure_unit = PM_Device.power_monitor_conf.MeasureUnit.Power, time=duration, reset=False)
            print(f"Power: {PM_Device.get_measured_data(unit = PM_Device.power_monitor_conf.MeasureUnit.POWER)} [W]")
            break
        else:
            print("measure_power not found.")

Unfortunately, I found a limitation in the onboard power monitor's firmware. Each 'start' command resets the target and starts the measurement, so it can't be triggered during the application. I need to find another solution.

bjarki-andreasen commented 1 day ago

measure with on-board adc is an options, and I from NXP would like to propose to use our mcu-link-pro as a standalone measure device to do this. see https://www.nxp.com/docs/en/user-manual/UM11673.pdf

Absolutely we need to support it :) flashing, voltage and current measurement in one device, really neat :)

Also, we could write a zephyr sample application which uses an on-board ADC for power measurement, our own "in-house" power probe firmware :)

bjarki-andreasen commented 1 day ago

@bjarki-andreasen FYI. I performed some exercises on triggering measurements from the Zephyr application.


void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
    /* Request measurement */
    printk("measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

    /* wait for duration */
    k_sleep(K_MSEC(duration_us));
}

ZTEST(testsuite, test_name_of_testcase)
{
    ztest_probe_power(2000000, 1, 3);
}

ZTEST_SUITE(testsuite, NULL, NULL, NULL, NULL, NULL);

pytest:


def test_foo(dut: DeviceAdapter, request):
    powershield_device = request.config.getoption("--powermonitor")
    PM_Device = PowerMonitor()
    PM_Device.setup(power_device_path = powershield_device)
    while True:
        lines = dut.readlines() 
        # Find the line containing 'measure_power'
        measure_power_line = next((line for line in lines if 'measure_power' in line), None)
        if measure_power_line:
            parts = measure_power_line.split()
            # Extract the values
            command = parts[0]  # "measure_power"
            duration = int(parts[1]) /1000000 # us
            print(duration)
            value2 = int(parts[2])  # 
            value3 = int(parts[3])  # 

            PM_Device.measure(measure_unit = PM_Device.power_monitor_conf.MeasureUnit.Power, time=duration, reset=False)
            print(f"Power: {PM_Device.get_measured_data(unit = PM_Device.power_monitor_conf.MeasureUnit.POWER)} [W]")
            break
        else:
            print("measure_power not found.")

Unfortunately, I found a limitation in the onboard power monitor's firmware. Each 'start' command resets the target and starts the measurement, so it can't be triggered during the application. I need to find another solution.

In that case, could the current measurement not be started and running constantly in the "background"? We don't need to actually start it only when we care about the samples right?

hakehuang commented 1 day ago

Absolutely we need to support it :) flashing, voltage and current measurement in one device, really neat :)

I will add same support when the initial pr merged https://github.com/zephyrproject-rtos/zephyr/pull/80692

hakehuang commented 1 day ago

Also, we could write a zephyr sample application which uses an on-board ADC for power measurement, our own "in-house" power probe firmware :)

it is difficult, for power measurement, we need some additional circuits, now only lpcxpresso55s36 has such circuits on board

gbarkadiusz commented 1 day ago

@bjarki-andreasen FYI. I performed some exercises on triggering measurements from the Zephyr application.


void ztest_probe_power(int duration_us, int min_uJ, int max_uJ)
{
    /* Request measurement */
    printk("measure_power %d %d %d\n", duration_us, min_uJ, max_uJ);

    /* wait for duration */
    k_sleep(K_MSEC(duration_us));
}

ZTEST(testsuite, test_name_of_testcase)
{
    ztest_probe_power(2000000, 1, 3);
}

ZTEST_SUITE(testsuite, NULL, NULL, NULL, NULL, NULL);

pytest:


def test_foo(dut: DeviceAdapter, request):
    powershield_device = request.config.getoption("--powermonitor")
    PM_Device = PowerMonitor()
    PM_Device.setup(power_device_path = powershield_device)
    while True:
        lines = dut.readlines() 
        # Find the line containing 'measure_power'
        measure_power_line = next((line for line in lines if 'measure_power' in line), None)
        if measure_power_line:
            parts = measure_power_line.split()
            # Extract the values
            command = parts[0]  # "measure_power"
            duration = int(parts[1]) /1000000 # us
            print(duration)
            value2 = int(parts[2])  # 
            value3 = int(parts[3])  # 

            PM_Device.measure(measure_unit = PM_Device.power_monitor_conf.MeasureUnit.Power, time=duration, reset=False)
            print(f"Power: {PM_Device.get_measured_data(unit = PM_Device.power_monitor_conf.MeasureUnit.POWER)} [W]")
            break
        else:
            print("measure_power not found.")

Unfortunately, I found a limitation in the onboard power monitor's firmware. Each 'start' command resets the target and starts the measurement, so it can't be triggered during the application. I need to find another solution.

In that case, could the current measurement not be started and running constantly in the "background"? We don't need to actually start it only when we care about the samples right?

That was my first thought. WIP. :)

bjarki-andreasen commented 1 day ago

Also, we could write a zephyr sample application which uses an on-board ADC for power measurement, our own "in-house" power probe firmware :)

it is difficult, for power measurement, we need some additional circuits, now only lpcxpresso55s36 has such circuits on board

We have devicetree models like voltage-divider, I would think we could add shunt-resistor or current-shunt-resistor or something to convert voltage to current :) some boards do have shunt resistors, or at least a very obvious jumper to replace with shunt resistor

gbarkadiusz commented 1 day ago

Also, we could write a zephyr sample application which uses an on-board ADC for power measurement, our own "in-house" power probe firmware :)

it is difficult, for power measurement, we need some additional circuits, now only lpcxpresso55s36 has such circuits on board

We have devicetree models like voltage-divider, I would think we could add shunt-resistor or current-shunt-resistor or something to convert voltage to current :) some boards do have shunt resistors, or at least a very obvious jumper to replace with shunt resistor

The problem is rather, the adc is not working in low power state.

bjarki-andreasen commented 1 day ago

Also, we could write a zephyr sample application which uses an on-board ADC for power measurement, our own "in-house" power probe firmware :)

it is difficult, for power measurement, we need some additional circuits, now only lpcxpresso55s36 has such circuits on board

We have devicetree models like voltage-divider, I would think we could add shunt-resistor or current-shunt-resistor or something to convert voltage to current :) some boards do have shunt resistors, or at least a very obvious jumper to replace with shunt resistor

The problem is rather, the adc is not working in low power state.

The https://www.nxp.com/docs/en/user-manual/UM11673.pdf is a standalone device, so it will be running a completely different firmware than the DUT, its not measuring itself :)

By on-board, I at least mean ADC on separate device used as probe, not ADC on the same board, that would be really challenging, especially since ADCs are not exactly the least power hungry peripherals themselves.