-
Notifications
You must be signed in to change notification settings - Fork 7k
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
POC for a new harness designed for power measurement purposes. #85130
base: main
Are you sure you want to change the base?
POC for a new harness designed for power measurement purposes. #85130
Conversation
679de9e
to
c474ca5
Compare
pytest_args: [--probe=stm_powershield, | ||
--probe-port=<path_to>/STMicroelectronics_PowerShield__Virtual_ComPort] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two args (--probe
and --probe-port
) should be provided via the command line, as they vary, as opposed to the pytest_dut_scope: session
and the properties in test_expected_values.yaml
file which are "portable"/"fixed" regardless of the probe and port used :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to move
- name: "wakeup_timer"
measurement_time: 2
num_of_transitions: 1
rms: [0.26, 140]
to
harness_config:
measurements:
- name: "wakeup_timer"
measurement_time: 2
num_of_transitions: 1
rms: [0.26, 140]
instead of the separate file? not sure what having the commands in a separate file acheives :)
4109da0
to
1424b32
Compare
@bjarki-andreasen Please take another look. I'm not sure that moving |
Would you argue the same for the --serial option and its parameters? The probe and probe-port are instances of actual hardware right? If i ran the same test locally, would I not need to change the port to match my setup? |
380d8c0
to
91fa4eb
Compare
Yes, you need to change the port to match your setup.
@bjarki-andreasen , @nashif Please take a look. |
f202877
to
c0e83bd
Compare
c0e83bd
to
8bab44b
Compare
cc @nordic-piks |
raise ValueError("Invalid unit. Use 'us', 'ms', or 's'.") | ||
|
||
@staticmethod | ||
def current_RMS(data, exclude_first_600=True, num_peaks=1): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, number of samples which needs to be excluded from analysis should be taken also from sample.yml,
so that this can be easily configures for given test.
Of course this is just a hint if you want to make this approach more general.
data = [float(x) for x in data] | ||
|
||
# Find the peaks in the data using the `find_peaks` function | ||
peaks = signal.find_peaks(data, distance=40, height=0.008)[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, minimal number of samples between peaks and its "hight" should be also taken from sample.yaml
Of course this is just a hint if you want to make this approach more general.
- name: "power states" | ||
measurement_time: 6 | ||
num_of_transitions: 4 | ||
rms: [56.0, 4.0, 1.3, 0.26, 140] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, it would be good to test/verify also the length of each period between peaks.
This will allow to verify if actual sleep time meets expectation (looking from current consumption measurements).
Also, you assume peaks of high current and measures current in between.
There is also beneficial to measure current during active time i.e. being able to specify requirements for that in test.
Of course this is just a hint if you want to make this approach more general :)
self.target_voltage = None | ||
self.target_temperature = None | ||
self.acqTimeoutThread = None | ||
self.power_shield_conf = PowerShieldConf() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, parameters for PowerShieldConf e.g. voltage, sampling frequency should be taken from some board configuration file (here they just base on defaults). I do not see mechanisms to provide it externally.
Possibly, board parameters should be overwritable by some specific configuration in the test (sample.yaml)
Of course this is just a hint if you want to make this approach more general :)
- name: "power states" | ||
measurement_time: 6 | ||
num_of_transitions: 4 | ||
rms: [56.0, 4.0, 1.3, 0.26, 140] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beside RMS, it would be also interesting to verify min and/or max value of current during some period.
This will allow to detect unexpected events (peaks) when you expect to have stable sleep current.
Of course this is just a hint if you want to make this approach more general.
@nordic-piks Thank you for your feedback. |
self.target_temperature = self.get_temperature(self.power_shield_conf.temperature_unit) | ||
self.target_voltage = self.get_voltage_level() | ||
|
||
def connect(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either declare all public methods in ABC base or make methods not referenced in ABC api private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to limit the function in ABC to init
measure
and get_data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In such case methods that do not override abstract methods should be private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
except Exception as e: | ||
logging.error(f"Failed to reopen connection after reset: {e}") | ||
|
||
def get_voltage_level(self) -> float: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specify the unit of the return value (V, mV etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return None | ||
else: | ||
logging.warning("No response for voltage command.") | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please return float('nan') in case of 'no response', None will crash any further calculations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Sends the temperature command and returns the temperature as a float. | ||
|
||
:param unit: The unit to request the temperature in, either 'degc' or 'degf'. | ||
:return: The temperature value as a float. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, please specify unit (C, K, F)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
else: | ||
logging.warning(f"Failed to set data format to {data_format}.") | ||
|
||
def set_frequency(self, frequency: str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change type of this 'frequency' argument to either float or 'enum' type.
If this is not direct frequency in Hz then change the argument name to something like 'frequency_option'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
else: | ||
logging.warning(f"Failed to set sampling frequency to {frequency}.") | ||
|
||
def set_acquire_time(self, acquire_time: str = '0'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: it should be: set_acquisition_time, acquire is a verb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, fixed.
else: | ||
logging.warning(f"Failed to set acquisition time to {acquire_time}.") | ||
|
||
def set_voltage(self, voltage: str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nitpick as for 'set_frequency'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
self.function_mode = function_mode | ||
self.target_voltage = target_voltage | ||
self.temperature_unit = temperature_unit | ||
self.acquisition_time = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please annotate as 'Optional[float]'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As below.
|
||
class PowerShieldData: | ||
def __init__(self): | ||
self.data = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please annotate 'data'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As below.
self.port = port | ||
self.baudrate = baudrate | ||
self.timeout = timeout | ||
self.serial_connection = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please annotate 'serial_connection'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you mean?
"Connection to %s at %d baud opened successfully.", self.port, self.baudrate | ||
) | ||
except serial.SerialException as e: | ||
logging.error("Error opening serial port %s: %s", self.port, str(e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why catch this error and not raise anything else. Test will continue with failed serial connection and will fail later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add simply raise should be enough?
Fixed.
|
||
:return: The processed received data as a string. | ||
""" | ||
s = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make meaningful variable name instead of 's'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
"""Closes the connection if the object is deleted.""" | ||
self.close() | ||
# Shut down logging | ||
logging.shutdown() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't call shutdown it will bring down entire logging system
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
|
||
class UnityFunctions: | ||
@staticmethod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all methods are static class in not needed, they could be declared as functions in UnityFunctions.py module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
50c4d38
to
a46de65
Compare
Add support for powerShield of stm32l562e_dk board. Signed-off-by: Arkadiusz Cholewinski <arkadiuszx.cholewinski@intel.com>
Add three tests. - power_states - power_wakeup_timer - power_residency_time Signed-off-by: Arkadiusz Cholewinski <arkadiuszx.cholewinski@intel.com>
a46de65
to
3ecc5a6
Compare
POC of Power twister harness.
This pr adds support for power measurements for twister and three example tests.
pm.states: Verifies the transition to all available power states on stm32l562e_dk.
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
pm.residency_time: Tests the residency time of each power state.
This test evaluates the residency time of different power states on the stm32l562e_dk board.
pm.wakeup_timer: 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.
Setup


To reproduce.:
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