From 68a7ba5d63a0b80e0cb0dfee3a66322824083620 Mon Sep 17 00:00:00 2001 From: "Kok, Chern Yee" Date: Mon, 14 Oct 2024 08:36:27 +0800 Subject: [PATCH 1/2] allow loading device without power data config --- backend/submodule/rs_device.py | 4 ++++ backend/submodule/rs_device_resources.py | 21 +++++++++++------ backend/submodule/rs_power_config.py | 29 ++++++++++++++++++------ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/backend/submodule/rs_device.py b/backend/submodule/rs_device.py index 794201dc..30d0cc36 100644 --- a/backend/submodule/rs_device.py +++ b/backend/submodule/rs_device.py @@ -236,6 +236,10 @@ def __init__(self, device): # soc peripherals module self.resources.register_module(ModuleType.SOC_PERIPHERALS, Peripheral_SubModule(self.resources)) + # skip compute output power and exit function if no power data available + if not self.resources.powercfg.is_loaded(): + return + # perform initial calculation self.compute_output_power() diff --git a/backend/submodule/rs_device_resources.py b/backend/submodule/rs_device_resources.py index 23af8892..83136028 100644 --- a/backend/submodule/rs_device_resources.py +++ b/backend/submodule/rs_device_resources.py @@ -2,9 +2,9 @@ import os from device.device_resource import Device from .rs_power_config import RsPowerConfig, ElementType, ScenarioType +from .rs_logger import log, RsLogLevel from utilities.common_utils import RsEnum, RsCustomException from dataclasses import dataclass, field -from typing import List class DeviceNotFoundException(RsCustomException): def __init__(self): @@ -166,15 +166,22 @@ class RsDeviceResources: def __init__(self, device): self.device: Device = device - self.powercfg = RsPowerConfig(self.get_power_config_filepath()) self.modules = [None, None, None, None, None, None, None] + self.powercfg = RsPowerConfig() + filepath = self.get_power_config_filepath() + if filepath: + self.powercfg.load(filepath) + else: + log(f"Device '{device.name}' power data element not found", RsLogLevel.WARNING) def get_power_config_filepath(self) -> str: - power_cfg_filepath = self.device.internals['power_data'].file - if not os.path.isabs(power_cfg_filepath): - return os.path.join(os.path.dirname(self.device.filepath), power_cfg_filepath) - else: - return power_cfg_filepath + if 'power_data' in self.device.internals: + power_cfg_filepath = self.device.internals['power_data'].file + if not os.path.isabs(power_cfg_filepath): + return os.path.join(os.path.dirname(self.device.filepath), power_cfg_filepath) + else: + return power_cfg_filepath + return None def get_peripheral_noc_power_factor(self, master: PeripheralType, slave: PeripheralType) -> float: return self.powercfg.get_coeff(ElementType.NOC, f'{master.value}.{slave.value}') diff --git a/backend/submodule/rs_power_config.py b/backend/submodule/rs_power_config.py index 9065c8c3..0f4a4042 100644 --- a/backend/submodule/rs_power_config.py +++ b/backend/submodule/rs_power_config.py @@ -36,6 +36,10 @@ class PowerConfigSchemaValidationException(RsCustomException): def __init__(self, ex: ValidationError): super().__init__(f"Parsing Error: {ex.messages}") +class PowerConfigNotAvailable(RsCustomException): + def __init__(self): + super().__init__(f"Power config data not available") + class ElementType(Enum): BRAM = 'bram' CLOCKING = 'clocking' @@ -169,25 +173,25 @@ def post_load(self, data, **kwargs): return RsPowerConfigData(**data) class RsPowerConfig: - def __init__(self, filepath: str) -> None: - self.filepath = filepath + def __init__(self) -> None: + self.filepath = None self.data: RsPowerConfigData = None - self.load() + self.loaded = False - def load(self) -> bool: + def load(self, filepath: str) -> bool: try: # read the main power config json file - with open(self.filepath, 'r') as fd: + with open(filepath, 'r') as fd: rawdata = json.load(fd) # resolve all $ref nodes - resolved_data = jsonref.replace_refs(rawdata, base_uri='file:///' + os.path.abspath(os.path.dirname(self.filepath)).replace('\\', '/') + '/') + resolved_data = jsonref.replace_refs(rawdata, base_uri='file:///' + os.path.abspath(os.path.dirname(filepath)).replace('\\', '/') + '/') # verify json structure data = RsPowerConfigDataSchema().load(resolved_data) # store data - self.data = data + self.filepath, self.data, self.loaded = filepath, data, True except FileNotFoundError as ex: raise PowerConfigFileNotFoundException(self.filepath) @@ -201,13 +205,24 @@ def load(self) -> bool: raise ex return True + def is_loaded(self) -> bool: + return self.loaded + def get_static_component(self, type: ElementType) -> RsStaticPowerElement: + # raise power data not available exception + if not self.loaded: + raise PowerConfigNotAvailable() + comps = [c for c in self.data.static if c.type == type] if comps: return comps[0] raise PowerConfigStaticComponentNotFoundException(type.value) def get_component(self, type: ElementType) -> RsDynamicPowerComponent: + # raise power data not available exception + if not self.loaded: + raise PowerConfigNotAvailable() + comps = [c for c in self.data.components if c.type == type] if comps: return comps[0] From ce2fc9a622e1740861912e01369b555942de248b Mon Sep 17 00:00:00 2001 From: "Kok, Chern Yee" Date: Mon, 14 Oct 2024 10:05:41 +0800 Subject: [PATCH 2/2] update power config unit tests --- tests/test_rs_power_config.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/test_rs_power_config.py b/tests/test_rs_power_config.py index 08d4dd9e..5919cc0c 100644 --- a/tests/test_rs_power_config.py +++ b/tests/test_rs_power_config.py @@ -17,50 +17,62 @@ PowerConfigPolynomialNotFoundException ) +def test_not_loaded(): + pwrcfg = RsPowerConfig() + assert False == pwrcfg.is_loaded() + def test_invalid_power_config_filepath(): with pytest.raises(PowerConfigFileNotFoundException): - RsPowerConfig(filepath='abc.json') + RsPowerConfig().load('abc.json') def test_invalid_json_content(): with pytest.raises(PowerConfigParsingException): - RsPowerConfig(filepath='tests/data/invalid_power_config.json') + RsPowerConfig().load('tests/data/invalid_power_config.json') def test_invalid_json_ref(): with pytest.raises(PowerConfigParsingException): - RsPowerConfig(filepath='tests/data/invalid_json_ref_power_config.json') + RsPowerConfig().load('tests/data/invalid_json_ref_power_config.json') def test_invalid_power_config_schema(): with pytest.raises(PowerConfigSchemaValidationException): - RsPowerConfig(filepath='tests/data/invalid_schema_power_config.json') + RsPowerConfig().load('tests/data/invalid_schema_power_config.json') def test_get_coeff_with_not_exist_component(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + pwrcfg.load('tests/data/power_config.json') with pytest.raises(PowerConfigComponentNotFoundException): pwrcfg.get_coeff(ElementType.FABRIC_LE, "TEST1") def test_get_coeff_with_not_exist_coeff(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + pwrcfg.load('tests/data/power_config.json') with pytest.raises(PowerConfigCoeffNotFoundException): pwrcfg.get_coeff(ElementType.DSP, "ABC") def test_get_coeff(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + pwrcfg.load('tests/data/power_config.json') assert 0.1234 == pwrcfg.get_coeff(ElementType.DSP, "TEST1") def test_get_polynomial_coeff_with_not_exist_component(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + pwrcfg.load('tests/data/power_config.json') with pytest.raises(PowerConfigStaticComponentNotFoundException): pwrcfg.get_polynomial_coeff(ElementType.NOC, ScenarioType.TYPICAL) def test_get_polynomial_coeff_with_not_exist_scenario(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + pwrcfg.load('tests/data/power_config.json') with pytest.raises(PowerConfigPolynomialNotFoundException): pwrcfg.get_polynomial_coeff(ElementType.CLB, ScenarioType.WORSE) def test_get_polynomial_coeff(): - pwrcfg = RsPowerConfig(filepath='tests/data/power_config.json') + pwrcfg = RsPowerConfig() + result = pwrcfg.load('tests/data/power_config.json') polynomials = pwrcfg.get_polynomial_coeff(ElementType.CLB, ScenarioType.TYPICAL) assert 1 == len(polynomials) + assert True == result + assert True == pwrcfg.is_loaded() assert 5 == polynomials[0].length assert 1.25 == polynomials[0].factor assert [0.1, 0.2, 0.3, 0.4, 0.5] == polynomials[0].coeffs