Skip to content
Merged
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
4 changes: 4 additions & 0 deletions backend/submodule/rs_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@
# 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

Check warning on line 241 in backend/submodule/rs_device.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_device.py#L240-L241

Added lines #L240 - L241 were not covered by tests

# perform initial calculation
self.compute_output_power()

Expand Down
21 changes: 14 additions & 7 deletions backend/submodule/rs_device_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -166,15 +166,22 @@

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)

Check warning on line 173 in backend/submodule/rs_device_resources.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_device_resources.py#L173

Added line #L173 was not covered by tests
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)

Check warning on line 181 in backend/submodule/rs_device_resources.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_device_resources.py#L179-L181

Added lines #L179 - L181 were not covered by tests
else:
return power_cfg_filepath

Check warning on line 183 in backend/submodule/rs_device_resources.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_device_resources.py#L183

Added line #L183 was not covered by tests
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}')
Expand Down
29 changes: 22 additions & 7 deletions backend/submodule/rs_power_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
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")

Check warning on line 41 in backend/submodule/rs_power_config.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_power_config.py#L41

Added line #L41 was not covered by tests

class ElementType(Enum):
BRAM = 'bram'
CLOCKING = 'clocking'
Expand Down Expand Up @@ -169,25 +173,25 @@
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)
Expand All @@ -201,13 +205,24 @@
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()

Check warning on line 214 in backend/submodule/rs_power_config.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_power_config.py#L214

Added line #L214 was not covered by tests

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()

Check warning on line 224 in backend/submodule/rs_power_config.py

View check run for this annotation

Codecov / codecov/patch

backend/submodule/rs_power_config.py#L224

Added line #L224 was not covered by tests

comps = [c for c in self.data.components if c.type == type]
if comps:
return comps[0]
Expand Down
32 changes: 22 additions & 10 deletions tests/test_rs_power_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading