Source code for pyEDAA.Reports.CodeCoverage.UCIS.Coverpoints

# ==================================================================================================================== #
#              _____ ____    _        _      ____                       _                                              #
#  _ __  _   _| ____|  _ \  / \      / \    |  _ \ ___ _ __   ___  _ __| |_ ___                                        #
# | '_ \| | | |  _| | | | |/ _ \    / _ \   | |_) / _ \ '_ \ / _ \| '__| __/ __|                                       #
# | |_) | |_| | |___| |_| / ___ \  / ___ \ _|  _ <  __/ |_) | (_) | |  | |_\__ \                                       #
# | .__/ \__, |_____|____/_/   \_\/_/   \_(_)_| \_\___| .__/ \___/|_|   \__|___/                                       #
# |_|    |___/                                        |_|                                                              #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2026-2026 Electronic Design Automation Abstraction (EDA²)                                                  #
#                                                                                                                      #
# Licensed under the Apache License, Version 2.0 (the "License");                                                      #
# you may not use this file except in compliance with the License.                                                     #
# You may obtain a copy of the License at                                                                              #
#                                                                                                                      #
#   http://www.apache.org/licenses/LICENSE-2.0                                                                         #
#                                                                                                                      #
# Unless required by applicable law or agreed to in writing, software                                                  #
# distributed under the License is distributed on an "AS IS" BASIS,                                                    #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                             #
# See the License for the specific language governing permissions and                                                  #
# limitations under the License.                                                                                       #
#                                                                                                                      #
# SPDX-License-Identifier: Apache-2.0                                                                                  #
# ==================================================================================================================== #
#
"""
The coverpoints of the covergroup coverage of the UCIS XML interchange format: their bins, and the value ranges or
sequences a bin counts.
"""
from __future__                                     import annotations

from typing                                         import TYPE_CHECKING, Iterable, Optional as Nullable, Self

from lxml.etree                                     import _Element
from pyTooling.Common                               import getFullyQualifiedName
from pyTooling.Decorators                           import export, readonly
from pyTooling.MetaClasses                          import ExtendedType, abstractclass

from pyEDAA.Reports.CodeCoverage.UCIS.Bins          import BinContents
from pyEDAA.Reports.CodeCoverage.UCIS.CoverOptions  import CoverpointOptions
from pyEDAA.Reports.CodeCoverage.UCIS.Elements      import Base, UserAttribute

if TYPE_CHECKING:
	from pyEDAA.Reports.CodeCoverage.UCIS.Covergroups import CovergroupInstance


[docs] @export @abstractclass class BinBase(Base): """ Base-class of the bins of a coverpoint or a cross: the bin's kind, alias, and - as some tools write them - name and key. """ _binType: str #: Kind of the bin, e.g. ``default``, ``ignore`` or ``illegal``. _alias: Nullable[str] #: An alias of the bin's name. _name: Nullable[str] #: Name of the bin. _key: Nullable[str] #: UCIS key of the bin.
[docs] def __init__( self, binType: str, alias: Nullable[str] = None, name: Nullable[str] = None, key: Nullable[str] = None, userAttributes: Nullable[Iterable[UserAttribute]] = None ) -> None: """ Initialize the bin's kind, alias, name and key. :param binType: Kind of the bin, e.g. ``default``, ``ignore`` or ``illegal``. :param alias: Optional, an alias of the bin's name. Default: ``None``. :param name: Optional, name of the bin; the standard's schema has none, pyucis writes it. Default: ``None``. :param key: Optional, UCIS key of the bin; the standard's schema has none, pyucis writes it. Default: ``None``. :param userAttributes: Optional, the user-defined attributes. Default: ``None``. :raises ValueError: If parameter ``binType`` is ``None``. :raises TypeError: If parameter ``binType`` isn't of type :class:`str`. :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. :raises TypeError: If parameter ``name`` isn't of type :class:`str`. :raises TypeError: If parameter ``key`` isn't of type :class:`str`. :raises TypeError: If parameter ``userAttributes`` isn't iterable. :raises TypeError: If parameter ``userAttributes`` contains an element not of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. """ super().__init__(userAttributes) if binType is None: raise ValueError("Parameter 'binType' is None.") elif not isinstance(binType, str): ex = TypeError("Parameter 'binType' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(binType)}'.") raise ex for parameterName, value in ( ("alias", alias), ("name", name), ("key", key) ): if value is not None and not isinstance(value, str): ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex self._binType = binType self._alias = alias self._name = name self._key = key
@readonly def BinType(self) -> str: """ Read-only property to access the kind of the bin (:attr:`_binType`). :returns: The kind, as stated, e.g. ``default``, ``ignore`` or ``illegal``. """ return self._binType @readonly def Alias(self) -> Nullable[str]: """ Read-only property to access the alias of the bin's name (:attr:`_alias`). :returns: The alias; ``None``, if the report states none. """ return self._alias @readonly def Name(self) -> Nullable[str]: """ Read-only property to access the name of the bin (:attr:`_name`). :returns: The name; ``None``, if the report states none. """ return self._name @readonly def Key(self) -> Nullable[str]: """ Read-only property to access the UCIS key of the bin (:attr:`_key`). :returns: The key; ``None``, if the report states none. """ return self._key
[docs] @export class ValueRange(metaclass=ExtendedType, slots=True): """ A ``<range>`` of a coverpoint's bin: a range of values, and how often the coverpoint's expression had one of them. """ _parent: Nullable[CoverpointBin] #: The coverpoint bin the range belongs to. _from: int #: The range's lowest value. _to: int #: The range's highest value. _contents: BinContents #: How often the expression had a value of the range.
[docs] def __init__(self, fromValue: int, toValue: int, contents: BinContents) -> None: """ Initialize the range of values. The coverpoint bin owning the range takes it as a parameter and becomes its parent. :param fromValue: The range's lowest value. :param toValue: The range's highest value; the lowest one again for a single value. :param contents: How often the expression had a value of the range. :raises ValueError: If parameter ``fromValue`` is ``None``. :raises TypeError: If parameter ``fromValue`` isn't of type :class:`int`. :raises ValueError: If parameter ``toValue`` is ``None``. :raises TypeError: If parameter ``toValue`` isn't of type :class:`int`. :raises ValueError: If parameter ``contents`` is ``None``. :raises TypeError: If parameter ``contents`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.BinContents`. """ for parameterName, value in ( ("fromValue", fromValue), ("toValue", toValue) ): if value is None: raise ValueError(f"Parameter '{parameterName}' is None.") elif not isinstance(value, int) or isinstance(value, bool): ex = TypeError(f"Parameter '{parameterName}' is not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex if contents is None: raise ValueError("Parameter 'contents' is None.") elif not isinstance(contents, BinContents): ex = TypeError("Parameter 'contents' is not of type 'BinContents'.") ex.add_note(f"Got type '{getFullyQualifiedName(contents)}'.") raise ex self._parent = None self._from = fromValue self._to = toValue self._contents = contents
[docs] @classmethod def Parse(cls, element: _Element) -> Self: """ Parse a range of values from its ``<range>`` element. :param element: The ``<range>`` element. :returns: The range. """ return cls( int(element.attrib["from"]), int(element.attrib["to"]), BinContents.Parse(element.find("{*}contents")) )
@readonly def Parent(self) -> Nullable[CoverpointBin]: """ Read-only property to access the coverpoint bin the range belongs to (:attr:`_parent`). :returns: The coverpoint bin; ``None``, if the range belongs to none. """ return self._parent @readonly def From(self) -> int: """ Read-only property to access the range's lowest value (:attr:`_from`). :returns: The value. """ return self._from @readonly def To(self) -> int: """ Read-only property to access the range's highest value (:attr:`_to`). :returns: The value. """ return self._to @readonly def Contents(self) -> BinContents: """ Read-only property to access how often the expression had a value of the range (:attr:`_contents`). :returns: The contents: the count and the tests. """ return self._contents
[docs] @export class ValueSequence(metaclass=ExtendedType, slots=True): """ A ``<sequence>`` of a coverpoint's bin: a sequence of values, and how often the coverpoint's expression had them one after the other. """ _parent: Nullable[CoverpointBin] #: The coverpoint bin the sequence belongs to. _values: list[int] #: The values, from the first one. _contents: BinContents #: How often the expression had the values one after the other.
[docs] def __init__(self, values: Iterable[int], contents: BinContents) -> None: """ Initialize the sequence of values. The coverpoint bin owning the sequence takes it as a parameter and becomes its parent. :param values: The values, from the first one. :param contents: How often the expression had the values one after the other. :raises ValueError: If parameter ``values`` is ``None``. :raises TypeError: If parameter ``values`` isn't iterable. :raises TypeError: If parameter ``values`` contains an element not of type :class:`int`. :raises ValueError: If parameter ``values`` is empty. :raises ValueError: If parameter ``contents`` is ``None``. :raises TypeError: If parameter ``contents`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.BinContents`. """ if contents is None: raise ValueError("Parameter 'contents' is None.") elif not isinstance(contents, BinContents): ex = TypeError("Parameter 'contents' is not of type 'BinContents'.") ex.add_note(f"Got type '{getFullyQualifiedName(contents)}'.") raise ex self._parent = None self._values = valueList = [] self._contents = contents if values is None: raise ValueError("Parameter 'values' is None.") elif not isinstance(values, Iterable): ex = TypeError("Parameter 'values' is not iterable.") ex.add_note(f"Got type '{getFullyQualifiedName(values)}'.") raise ex for value in values: if not isinstance(value, int) or isinstance(value, bool): ex = TypeError("Parameter 'values' contains an element not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex valueList.append(value) if len(self._values) == 0: raise ValueError("Parameter 'values' is empty.")
[docs] @classmethod def Parse(cls, element: _Element) -> Self: """ Parse a sequence of values from its ``<sequence>`` element. :param element: The ``<sequence>`` element. :returns: The sequence. """ return cls( [int(valueElement.text) for valueElement in element.iterfind("{*}seqValue")], BinContents.Parse(element.find("{*}contents")) )
@readonly def Parent(self) -> Nullable[CoverpointBin]: """ Read-only property to access the coverpoint bin the sequence belongs to (:attr:`_parent`). :returns: The coverpoint bin; ``None``, if the sequence belongs to none. """ return self._parent @readonly def Values(self) -> list[int]: """ Read-only property to access the values (:attr:`_values`). :returns: The values, from the first one. """ return self._values @readonly def Contents(self) -> BinContents: """ Read-only property to access how often the expression had the values one after the other (:attr:`_contents`). :returns: The contents: the count and the tests. """ return self._contents
[docs] @export class CoverpointBin(BinBase): """ A ``<coverpointBin>`` of a coverpoint: its kind, and the ranges or sequences of values it counts. """ _parent: Nullable[Coverpoint] #: The coverpoint the bin belongs to. _ranges: list[ValueRange] #: The ranges of values the bin counts. _sequences: list[ValueSequence] #: The sequences of values the bin counts.
[docs] def __init__( self, binType: str, ranges: Nullable[Iterable[ValueRange]] = None, sequences: Nullable[Iterable[ValueSequence]] = None, alias: Nullable[str] = None, name: Nullable[str] = None, key: Nullable[str] = None, userAttributes: Nullable[Iterable[UserAttribute]] = None, *, parent: Nullable[Coverpoint] = None ) -> None: """ Initialize the coverpoint bin, become its ranges' and sequences' parent, and append it to the bins of its coverpoint. :param binType: Kind of the bin, e.g. ``default``, ``ignore`` or ``illegal``. :param ranges: Optional, the ranges of values the bin counts. Default: ``None``. :param sequences: Optional, the sequences of values the bin counts. Default: ``None``. :param alias: Optional, an alias of the bin's name. Default: ``None``. :param name: Optional, name of the bin, as pyucis writes it. Default: ``None``. :param key: Optional, UCIS key of the bin, as pyucis writes it. Default: ``None``. :param userAttributes: Optional, the user-defined attributes. Default: ``None``. :param parent: Optional, the coverpoint the bin belongs to. Default: ``None``. :raises ValueError: If parameter ``binType`` is ``None``. :raises TypeError: If parameter ``binType`` isn't of type :class:`str`. :raises TypeError: If parameter ``ranges`` isn't iterable. :raises TypeError: If parameter ``ranges`` contains an element not of type :class:`ValueRange`. :raises TypeError: If parameter ``sequences`` isn't iterable. :raises TypeError: If parameter ``sequences`` contains an element not of type :class:`ValueSequence`. :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. :raises TypeError: If parameter ``name`` isn't of type :class:`str`. :raises TypeError: If parameter ``key`` isn't of type :class:`str`. :raises TypeError: If parameter ``userAttributes`` isn't iterable. :raises TypeError: If parameter ``userAttributes`` contains an element not of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. :raises TypeError: If parameter ``parent`` isn't of type :class:`Coverpoint`. """ super().__init__(binType, alias, name, key, userAttributes) if parent is not None and not isinstance(parent, Coverpoint): ex = TypeError("Parameter 'parent' is not of type 'Coverpoint'.") ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") raise ex self._parent = parent self._ranges = rangeList = [] self._sequences = sequenceList = [] if ranges is not None: if not isinstance(ranges, Iterable): ex = TypeError("Parameter 'ranges' is not iterable.") ex.add_note(f"Got type '{getFullyQualifiedName(ranges)}'.") raise ex for valueRange in ranges: if not isinstance(valueRange, ValueRange): ex = TypeError("Parameter 'ranges' contains an element not of type 'ValueRange'.") ex.add_note(f"Got type '{getFullyQualifiedName(valueRange)}'.") raise ex valueRange._parent = self rangeList.append(valueRange) if sequences is not None: if not isinstance(sequences, Iterable): ex = TypeError("Parameter 'sequences' is not iterable.") ex.add_note(f"Got type '{getFullyQualifiedName(sequences)}'.") raise ex for valueSequence in sequences: if not isinstance(valueSequence, ValueSequence): ex = TypeError("Parameter 'sequences' contains an element not of type 'ValueSequence'.") ex.add_note(f"Got type '{getFullyQualifiedName(valueSequence)}'.") raise ex valueSequence._parent = self sequenceList.append(valueSequence) if parent is not None: parent._bins.append(self)
[docs] @classmethod def Parse(cls, element: _Element, *, parent: Nullable[Coverpoint] = None) -> Self: """ Parse a coverpoint bin, its ranges and sequences from its ``<coverpointBin>`` element. :param element: The ``<coverpointBin>`` element. :param parent: Optional, the coverpoint the bin belongs to. Default: ``None``. :returns: The coverpoint bin. :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. """ return cls( element.attrib["type"], [ValueRange.Parse(rangeElement) for rangeElement in element.iterfind("{*}range")], [ValueSequence.Parse(sequenceElement) for sequenceElement in element.iterfind("{*}sequence")], element.attrib.get("alias"), element.attrib.get("name"), element.attrib.get("key"), cls._ParseUserAttributes(element), parent=parent )
@readonly def Parent(self) -> Nullable[Coverpoint]: """ Read-only property to access the coverpoint the bin belongs to (:attr:`_parent`). :returns: The coverpoint; ``None``, if the bin belongs to none. """ return self._parent @readonly def Ranges(self) -> list[ValueRange]: """ Read-only property to access the ranges of values the bin counts (:attr:`_ranges`). :returns: The ranges, in the order the report lists them; empty for a bin of sequences. """ return self._ranges @readonly def Sequences(self) -> list[ValueSequence]: """ Read-only property to access the sequences of values the bin counts (:attr:`_sequences`). :returns: The sequences, in the order the report lists them; empty for a bin of ranges. """ return self._sequences
[docs] @export class Coverpoint(Base): """ A ``<coverpoint>`` of a covergroup instance: its expression, options and bins. """ _parent: Nullable[CovergroupInstance] #: The covergroup instance the coverpoint belongs to. _name: str #: Name of the coverpoint. _key: str #: UCIS key of the coverpoint. _options: CoverpointOptions #: The options of the coverpoint. _alias: Nullable[str] #: An alias of the coverpoint's name. _expression: Nullable[str] #: The expression the coverpoint samples. _bins: list[CoverpointBin] #: The bins.
[docs] def __init__( self, name: str, key: str, options: CoverpointOptions, alias: Nullable[str] = None, expression: Nullable[str] = None, userAttributes: Nullable[Iterable[UserAttribute]] = None, *, parent: Nullable[CovergroupInstance] = None ) -> None: """ Initialize the coverpoint, and append it to the coverpoints of its covergroup instance. Its bins are added by creating them with this coverpoint as their parent. :param name: Name of the coverpoint, e.g. ``cvpt_a``. :param key: UCIS key of the coverpoint. :param options: The options of the coverpoint. :param alias: Optional, an alias of the coverpoint's name. Default: ``None``. :param expression: Optional, the expression the coverpoint samples, e.g. ``a``. Default: ``None``. :param userAttributes: Optional, the user-defined attributes. Default: ``None``. :param parent: Optional, the covergroup instance the coverpoint belongs to. Default: ``None``. :raises ValueError: If parameter ``name`` is ``None``. :raises TypeError: If parameter ``name`` isn't of type :class:`str`. :raises ValueError: If parameter ``key`` is ``None``. :raises TypeError: If parameter ``key`` isn't of type :class:`str`. :raises ValueError: If parameter ``options`` is ``None``. :raises TypeError: If parameter ``options`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.CoverOptions.CoverpointOptions`. :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. :raises TypeError: If parameter ``expression`` isn't of type :class:`str`. :raises TypeError: If parameter ``userAttributes`` isn't iterable. :raises TypeError: If parameter ``userAttributes`` contains an element not of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. :raises TypeError: If parameter ``parent`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Covergroups.CovergroupInstance`. """ from pyEDAA.Reports.CodeCoverage.UCIS.Covergroups import CovergroupInstance super().__init__(userAttributes) for parameterName, value in ( ("name", name), ("key", key) ): if value is None: raise ValueError(f"Parameter '{parameterName}' is None.") elif not isinstance(value, str): ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex if options is None: raise ValueError("Parameter 'options' is None.") elif not isinstance(options, CoverpointOptions): ex = TypeError("Parameter 'options' is not of type 'CoverpointOptions'.") ex.add_note(f"Got type '{getFullyQualifiedName(options)}'.") raise ex for parameterName, value in ( ("alias", alias), ("expression", expression) ): if value is not None and not isinstance(value, str): ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex if parent is not None and not isinstance(parent, CovergroupInstance): ex = TypeError("Parameter 'parent' is not of type 'CovergroupInstance'.") ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") raise ex self._parent = parent self._name = name self._key = key self._options = options self._alias = alias self._expression = expression self._bins = [] if parent is not None: parent._coverpoints.append(self)
[docs] @classmethod def Parse(cls, element: _Element, *, parent: Nullable[CovergroupInstance] = None) -> Self: """ Parse a coverpoint, its options and bins from its ``<coverpoint>`` element. :param element: The ``<coverpoint>`` element. :param parent: Optional, the covergroup instance the coverpoint belongs to. Default: ``None``. :returns: The coverpoint. :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. """ coverpoint = cls( element.attrib["name"], element.attrib["key"], CoverpointOptions.Parse(element.find("{*}options")), element.attrib.get("alias"), element.attrib.get("exprString"), cls._ParseUserAttributes(element), parent=parent ) for binElement in element.iterfind("{*}coverpointBin"): CoverpointBin.Parse(binElement, parent=coverpoint) return coverpoint
@readonly def Parent(self) -> Nullable[CovergroupInstance]: """ Read-only property to access the covergroup instance the coverpoint belongs to (:attr:`_parent`). :returns: The covergroup instance; ``None``, if the coverpoint belongs to none. """ return self._parent @readonly def Name(self) -> str: """ Read-only property to access the name of the coverpoint (:attr:`_name`). :returns: The name, as declared. """ return self._name @readonly def Key(self) -> str: """ Read-only property to access the UCIS key of the coverpoint (:attr:`_key`). :returns: The key, as stated. """ return self._key @readonly def Options(self) -> CoverpointOptions: """ Read-only property to access the options of the coverpoint (:attr:`_options`). :returns: The options. """ return self._options @readonly def Alias(self) -> Nullable[str]: """ Read-only property to access the alias of the coverpoint's name (:attr:`_alias`). :returns: The alias; ``None``, if the report states none. """ return self._alias @readonly def Expression(self) -> Nullable[str]: """ Read-only property to access the expression the coverpoint samples (:attr:`_expression`). :returns: The expression, e.g. ``a``; ``None``, if the report states none. """ return self._expression @readonly def Bins(self) -> list[CoverpointBin]: """ Read-only property to access the bins (:attr:`_bins`). :returns: The bins, in the order the report lists them. """ return self._bins