Skip to content

Commit

Permalink
Change how coverpoints and covergroups are instanced (#49)
Browse files Browse the repository at this point in the history
Coverpoints and covergroups are now instanced by using _init(), called
by the add_* functions.
CoverTop has been added for the top of coverage. 
default names, descriptions and motivations (along with tier and tags)
can now be part of the coverpoint/covergroup code. And can be overridden
when adding to a covergroup.
  • Loading branch information
Alldred authored Sep 12, 2024
1 parent 1b0daa2 commit afa83c6
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 264 deletions.
4 changes: 2 additions & 2 deletions bucket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from . import rw
from .axisutils import AxisUtils
from .context import CoverageContext
from .covergroup import Covergroup
from .covergroup import Covergroup, Covertop
from .coverpoint import Coverpoint
from .sampler import Sampler

assert all((CoverageContext, Sampler, Covergroup, Coverpoint, AxisUtils, rw))
assert all((CoverageContext, Sampler, Covergroup, Coverpoint, Covertop, AxisUtils, rw))
5 changes: 3 additions & 2 deletions bucket/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@


class CoverBase:
name: str
NAME: str | None = None
DESCRIPTION: str = ""

full_path: str
description: str
target: int
hits: int

Expand Down
305 changes: 174 additions & 131 deletions bucket/covergroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import hashlib
import itertools
from types import SimpleNamespace
from types import NoneType, SimpleNamespace
from typing import TYPE_CHECKING, Callable, Iterable

from pydantic import validate_call
Expand All @@ -21,18 +21,20 @@
class Covergroup(CoverBase):
"""This class groups coverpoints together, and adds them to the hierarchy"""

@validate_call
def __init__(self, name: str, description: str):
def _init(
self, name: str | None = None, description: str | None = None, parent=None
):
"""
Parameters:
name: Name of covergroup
description: Description of covergroup
"""

self.name = name
self.description = description
self.name = name or self.NAME or type(self).__name__
self.description = description if description is not None else self.DESCRIPTION

# Required for top covergroup - will be overwritten for all others
self.full_path = name.lower()
self.full_path = self.name.lower()

self.tier = None
self.tier_active = True
Expand All @@ -50,7 +52,7 @@ def _setup(self):
This calls the user defined setup() plus any other setup required
"""
self.setup(ctx=CoverageContext.get())
self._set_full_path()
self._set_full_path_for_children()
self._update_tags_and_tiers()

def setup(self, ctx: SimpleNamespace):
Expand All @@ -67,7 +69,7 @@ def _update_tags_and_tiers(self):
if tag not in self.tags:
self.tags.append(tag)

def _set_full_path(self):
def _set_full_path_for_children(self):
"""
Set full_path strings for each child
"""
Expand All @@ -76,123 +78,7 @@ def _set_full_path(self):

for cg in self.covergroups.values():
cg.full_path = self.full_path + f".{cg.name.lower()}"
cg._set_full_path()

@validate_call
def include_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Enable coverpoints which match the provided function. Unmatched coverpoints will
not have their active state changed, except this is the first filter to be
applied, then any coverpoints which do not match are explicitly set to inactive,
as the default state is active.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
mismatch_state = None
if not self._filter_applied:
mismatch_state = False
self._apply_filter(matcher, True, mismatch_state)
return self

@validate_call
def restrict_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Filter coverpoints which match the provided function. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
self._apply_filter(matcher, None, False)
return self

@validate_call
def exclude_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Disable coverpoints which match the provided function. Unmatched coverpoints will
not have their active state changed.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
self._apply_filter(matcher, False, None)
return self

@validate_call
def include_by_name(self, names: MatchStrs):
"""
Enable coverpoints which match the provided names. Unmatched coverpoints will not
have their active state changed, except this is the first filter to be applied,
then any coverpoints which do not match are explicitly set to inactive, as the
default state is active.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.include_by_function(self._match_by_name(names))

@validate_call
def restrict_by_name(self, names: MatchStrs):
"""
Filter coverpoints which match the provided names. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.exclude_by_function(self._match_by_name(names))

@validate_call
def exclude_by_name(self, names: MatchStrs):
"""
Disable coverpoints which match the provided names. Unmatched coverpoints will
not have their active state changed.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.exclude_by_function(self._match_by_name(names))

@validate_call
def include_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Enable coverpoints which match the provided tags. Unmatched coverpoints will not
have their active state changed, except this is the first filter to be applied,
then any coverpoints which do not match are explicitly set to inactive, as the
default state is active.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match.
If cleared, any tags can match (default: False)
"""
return self.include_by_function(self._match_by_tags(tags, match_all))

@validate_call
def restrict_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Filter coverpoints which match the provided tags. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match. If cleared, any tags can match
"""
return self.exclude_by_function(self._match_by_tags(tags, match_all))

@validate_call
def exclude_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Disable coverpoints which match the provided tags. Unmatched coverpoints will not
have their active state changed.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match. If cleared, any tags can match
"""
return self.exclude_by_function(self._match_by_tags(tags, match_all))

@validate_call
def set_tier_level(self, tier: int):
"""
Filter the coverage tree for coverpoints equal to or less than 'tier'.
Parameters:
tier: The highest tier level to be set active
"""
self._set_tier_level(tier)
return self
cg._set_full_path_for_children()

def _set_tier_level(self, tier: int):
any_children_active = False
Expand Down Expand Up @@ -247,26 +133,60 @@ def _apply_filter(
self.active = any_children_active
return self.active

def add_coverpoint(self, coverpoint: "Coverpoint"):
def add_coverpoint(
self,
coverpoint: "Coverpoint",
name: str | None = None,
description: str | None = None,
motivation: str | None = None,
):
"""
Add a coverpoint instance to the covergroup
Parameters:
coverpoint: instance of a coverpoint
"""
name: [Optional]: Override the name
description: [Optional]: Override the description
motivation: [Optional]: Override the motivation
"""
assert isinstance(
name, str | NoneType
), f"name must be a string, not {type(name)}"
assert isinstance(
description, str | NoneType
), f"description must be a string, not {type(description)}"
assert isinstance(
motivation, str | NoneType
), f"motivation must be a string, not {type(motivation)}"
coverpoint._init(
name=name, description=description, motivation=motivation, parent=self
)

if coverpoint.name in self.coverpoints:
raise Exception("Coverpoint names must be unique within a covergroup")
coverpoint.parent = self
self.coverpoints[coverpoint.name] = coverpoint

def add_covergroup(self, covergroup: "Covergroup"):
def add_covergroup(
self,
covergroup: "Covergroup",
name: str | None = None,
description: str | None = None,
):
"""
Add a covergroup instance to the covergroup
Parameters:
covergroup: instance of a covergroup
"""
name: [Optional]: Override the name
description: [Optional]: Override the description
"""
assert isinstance(
name, str | NoneType
), f"name must be a string, not {type(name)}"
assert isinstance(
description, str | NoneType
), f"description must be a string, not {type(description)}"
covergroup._init(name=name, description=description, parent=self)
if covergroup.name in self.covergroups:
raise Exception("Covergroup names must be unique within a covergroup")
covergroup.parent = self
self.covergroups[covergroup.name] = covergroup

def __getattr__(self, key: str):
Expand Down Expand Up @@ -343,3 +263,126 @@ def _chain_run(self, start: OpenLink[CovRun] | None = None) -> Link[CovRun]:
child_close = child._chain_run(child_start)
child_start = child_close.link_across()
return start.close(self, child=child_close, link=CovRun(point=1), typ=CoverBase)


class Covertop(Covergroup):
"""This is for the top of the coverage tree"""

def __init__(self):
self._init()

@validate_call
def include_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Enable coverpoints which match the provided function. Unmatched coverpoints will
not have their active state changed, except this is the first filter to be
applied, then any coverpoints which do not match are explicitly set to inactive,
as the default state is active.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
mismatch_state = None
if not self._filter_applied:
mismatch_state = False
self._apply_filter(matcher, True, mismatch_state)
return self

@validate_call
def restrict_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Filter coverpoints which match the provided function. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
self._apply_filter(matcher, None, False)
return self

@validate_call
def exclude_by_function(self, matcher: Callable[[CoverBase], bool]):
"""
Disable coverpoints which match the provided function. Unmatched coverpoints will
not have their active state changed.
Parameters:
matcher: A function to match against coverpoint/covergroup data
"""
self._apply_filter(matcher, False, None)
return self

@validate_call
def include_by_name(self, names: MatchStrs):
"""
Enable coverpoints which match the provided names. Unmatched coverpoints will not
have their active state changed, except this is the first filter to be applied,
then any coverpoints which do not match are explicitly set to inactive, as the
default state is active.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.include_by_function(self._match_by_name(names))

@validate_call
def restrict_by_name(self, names: MatchStrs):
"""
Filter coverpoints which match the provided names. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.exclude_by_function(self._match_by_name(names))

@validate_call
def exclude_by_name(self, names: MatchStrs):
"""
Disable coverpoints which match the provided names. Unmatched coverpoints will
not have their active state changed.
Parameters:
names: A case-insensitive string or string list to match against
"""
return self.exclude_by_function(self._match_by_name(names))

@validate_call
def include_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Enable coverpoints which match the provided tags. Unmatched coverpoints will not
have their active state changed, except this is the first filter to be applied,
then any coverpoints which do not match are explicitly set to inactive, as the
default state is active.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match.
If cleared, any tags can match (default: False)
"""
return self.include_by_function(self._match_by_tags(tags, match_all))

@validate_call
def restrict_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Filter coverpoints which match the provided tags. Those that match will not
change their active state, those that don't match will be set to inactive.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match. If cleared, any tags can match
"""
return self.exclude_by_function(self._match_by_tags(tags, match_all))

@validate_call
def exclude_by_tags(self, tags: TagStrs, match_all: bool = False):
"""
Disable coverpoints which match the provided tags. Unmatched coverpoints will not
have their active state changed.
Parameters:
tags: Tag(s) to match against
match_all: If set, all tags must match. If cleared, any tags can match
"""
return self.exclude_by_function(self._match_by_tags(tags, match_all))

@validate_call
def set_tier_level(self, tier: int):
"""
Filter the coverage tree for coverpoints equal to or less than 'tier'.
Parameters:
tier: The highest tier level to be set active
"""
self._set_tier_level(tier)
return self
Loading

0 comments on commit afa83c6

Please sign in to comment.