Source code for avl_axi._coverage

# Copyright 2025 Apheleia
#
# Description:
# Apheleia Verification Library Coverage

from typing import Any

import avl

from ._item import ReadItem, WriteItem
from ._types import axi_atomic_t, axi_burst_t, axi_domain_t, axi_resp_t, axi_secsid_t


[docs] class Coverage(avl.Component):
[docs] def __init__(self, name: str, parent: avl.Component) -> None: """ Initialize Coverage :param name: Name of the coverage class. :type name: str :param parent: Parent component. :type parent: Component """ super().__init__(name, parent) self.item_port = avl.List() # Define Write coverage self.item = WriteItem("coverage_item", self) self.cg = avl.Covergroup("axi", self) self.cg.set_comment("AXI Coverage") #AWID self.add_coverpoint(self.cg, "awid") # AWADDR self.add_coverpoint(self.cg, "awaddr") # AWREGION self.add_coverpoint(self.cg, "awregion") # AWLEN self.add_coverpoint(self.cg, "awlen") # Simplified AWLEN for crosses if hasattr(self.item, "awlen"): self.cg.add_coverpoint("w_is_burst", lambda: self.item.get("awlen", default=None)) self.cg._cps_["w_is_burst"].add_bin("0", lambda x : x == 0) self.cg._cps_["w_is_burst"].add_bin("1", lambda x : x != 0) # AWSIZE self.add_coverpoint(self.cg, "awsize") # AWLEN X AWSIZE self.add_covercross(self.cg, "awlen==0 X awsize", "w_is_burst", "awsize") # AWBURST self.add_coverpoint(self.cg, "awburst", signal_type=axi_burst_t) # AWLEN X AWBURST self.add_covercross(self.cg, "awlen==0 X awburst", "w_is_burst", "awburst") # AWLOCK self.add_coverpoint(self.cg, "awlock") # AWLEN X AWLOCK self.add_covercross(self.cg, "awlen==0 X awlock", "w_is_burst", "awlock") # AWPROT self.add_coverpoint(self.cg, "awprot") # AWNSE self.add_coverpoint(self.cg, "awnse") # AWQOS self.add_coverpoint(self.cg, "awqos") # AWUSER self.add_coverpoint(self.cg, "awuser") # AWDOMAIN self.add_coverpoint(self.cg, "awdomain", signal_type=axi_domain_t) # AWSNOOP self.add_coverpoint(self.cg, "awsnoop") # AWSSTASHNID self.add_coverpoint(self.cg, "awstashnid") # AWSSTASHNIDEN self.add_coverpoint(self.cg, "awstashniden") # AWSSTASHLPID self.add_coverpoint(self.cg, "awstashlpid") # AWSSTASHLPIDEN self.add_coverpoint(self.cg, "awstashlpiden") # AWTRACE self.add_coverpoint(self.cg, "awtrace") # AWLOOP self.add_coverpoint(self.cg, "awloop") # AWMMUVLAID self.add_coverpoint(self.cg, "awmmuvalid") # AWMMUSECSID self.add_coverpoint(self.cg, "awmmusecsid", signal_type=axi_secsid_t) # AWMMUSID self.add_coverpoint(self.cg, "awmmusid") # AWMMUSSIDV self.add_coverpoint(self.cg, "awmmussidv") # AWMMUSSID self.add_coverpoint(self.cg, "awmmussid") # AWMMUATST self.add_coverpoint(self.cg, "awmmuatst") # AWMMFLOW self.add_coverpoint(self.cg, "awmmflow") # AWPBHA self.add_coverpoint(self.cg, "awpbha") # AWMECID self.add_coverpoint(self.cg, "awmecid") # AWNSAID self.add_coverpoint(self.cg, "awnsaid") # AWSUBSYSID self.add_coverpoint(self.cg, "awsubsysid") # AWATOP self.add_coverpoint(self.cg, "awatop", signal_type=axi_atomic_t) # AWLEN X AWATOP self.add_covercross(self.cg, "awlen==0 X awatop", "w_is_burst", "awatop") # AWMPAM self.add_coverpoint(self.cg, "awmpam") # AWIDUNQ self.add_coverpoint(self.cg, "awidunq") # AWCMO self.add_coverpoint(self.cg, "awcmo") # AWTAGOP self.add_coverpoint(self.cg, "awtagop") # AWTAG self.add_coverpoint(self.cg, "awtag") # AWTAGUPDATE self.add_coverpoint(self.cg, "awtagupdate") # AW Wait self.cg.add_coverpoint("aw_wait", lambda: self.item.get("aw_wait_cycles", default=None)) self.cg._cps_["aw_wait"].set_comment("AW WAIT CYCLES") for i in range(3): self.cg._cps_["aw_wait"].add_bin(f"{i}", i) self.cg._cps_["aw_wait"].add_bin("wait_cycles", range(0,256), stats=True) # WDATA self.add_coverpoint(self.cg, "wdata") # WSTRB self.add_coverpoint(self.cg, "wstrb") # WUSER self.add_coverpoint(self.cg, "wuser") # WPOISON self.add_coverpoint(self.cg, "wpoison") # WTRACE self.add_coverpoint(self.cg, "wtrace") # W Wait self.cg.add_coverpoint("w_wait[0]", lambda: self.item.get("w_wait_cycles", idx=0, default=None)) self.cg._cps_["w_wait[0]"].set_comment("W WAIT CYCLES (1st beat)") for i in range(3): self.cg._cps_["w_wait[0]"].add_bin(f"{i}", i) self.cg._cps_["w_wait[0]"].add_bin("wait_cycles", range(0,256), stats=True) # BRESP self.add_coverpoint(self.cg, "bresp", signal_type=axi_resp_t) # BCOMP self.add_coverpoint(self.cg, "bcomp") # BPERSIST self.add_coverpoint(self.cg, "bpersist") # BUSER self.add_coverpoint(self.cg, "buser") # BTRACE self.add_coverpoint(self.cg, "btrace") # BLOOP self.add_coverpoint(self.cg, "bloop") # BTAGMATCH self.add_coverpoint(self.cg, "btagmatch") # BWait self.cg.add_coverpoint("b_wait", lambda: self.item.get("b_wait_cycles", default=None)) self.cg._cps_["b_wait"].set_comment("B WAIT CYCLES") for i in range(3): self.cg._cps_["b_wait"].add_bin(f"{i}", i) self.cg._cps_["b_wait"].add_bin("wait_cycles", range(0,256), stats=True) # Define Read coverage self.item = ReadItem("coverage_item", self) #ARID self.add_coverpoint(self.cg, "arid") # ARADDR self.add_coverpoint(self.cg, "araddr") # ARREGION self.add_coverpoint(self.cg, "arregion") # ARLEN self.add_coverpoint(self.cg, "arlen") # Simplified ARLEN for crosses if hasattr(self.item, "arlen"): self.cg.add_coverpoint("r_is_burst", lambda: self.item.get("arlen", default=None)) self.cg._cps_["r_is_burst"].add_bin("0", lambda x : x == 0) self.cg._cps_["r_is_burst"].add_bin("1", lambda x : x != 0) # ARSIZE self.add_coverpoint(self.cg, "arsize") # ARLEN X ARSIZE self.add_covercross(self.cg, "arlen==0 X arsize", "r_is_burst", "arsize") # ARBURST self.add_coverpoint(self.cg, "arburst", signal_type=axi_burst_t) # ARLEN X ARBURST self.add_covercross(self.cg, "arlen==0 X arburst", "r_is_burst", "arburst") # ARLOCK self.add_coverpoint(self.cg, "arlock") # ARLEN X ARLOCK self.add_covercross(self.cg, "arlen==0 X arlock", "r_is_burst", "arlock") # ARPROT self.add_coverpoint(self.cg, "arprot") # ARNSE self.add_coverpoint(self.cg, "arnse") # ARQOS self.add_coverpoint(self.cg, "arqos") # ARUSER self.add_coverpoint(self.cg, "aruser") # ARDOMAIN self.add_coverpoint(self.cg, "ardomain", signal_type=axi_domain_t) # ARSNOOP self.add_coverpoint(self.cg, "arsnoop") # ARTRACE self.add_coverpoint(self.cg, "artrace") # ARLOOP self.add_coverpoint(self.cg, "arloop") # ARMMUVLAID self.add_coverpoint(self.cg, "armmuvalid") # ARMMUSECSID self.add_coverpoint(self.cg, "armmusecsid", signal_type=axi_secsid_t) # ARMMUSID self.add_coverpoint(self.cg, "armmusid") # ARMMUSSIDV self.add_coverpoint(self.cg, "armmussidv") # ARMMUSSID self.add_coverpoint(self.cg, "armmussid") # ARMMUATST self.add_coverpoint(self.cg, "armmuatst") # ARMMFLOW self.add_coverpoint(self.cg, "armmflow") # ARPBHA self.add_coverpoint(self.cg, "arpbha") # ARMECID self.add_coverpoint(self.cg, "armecid") # ARNSAID self.add_coverpoint(self.cg, "arnsaid") # ARSUBSYSID self.add_coverpoint(self.cg, "arsubsysid") # ARMPAM self.add_coverpoint(self.cg, "armpam") # ARIDUNQ self.add_coverpoint(self.cg, "aridunq") # ARTAGOP self.add_coverpoint(self.cg, "artagop") # ARTAG self.add_coverpoint(self.cg, "artag") # AR Wait self.cg.add_coverpoint("ar_wait", lambda: self.item.get("ar_wait_cycles", default=None)) self.cg._cps_["ar_wait"].set_comment("AR WAIT CYCLES") for i in range(3): self.cg._cps_["ar_wait"].add_bin(f"{i}", i) self.cg._cps_["ar_wait"].add_bin("wait_cycles", range(0,256), stats=True) # RDATA self.add_coverpoint(self.cg, "rdata") # RUSER self.add_coverpoint(self.cg, "ruser") # RPOISON self.add_coverpoint(self.cg, "rpoison") # RTRACE self.add_coverpoint(self.cg, "rtrace") # RRESP self.add_coverpoint(self.cg, "rresp") # Use bitwise as a list # RUSER self.add_coverpoint(self.cg, "ruser") # RTRACE self.add_coverpoint(self.cg, "rtrace") # RLOOP self.add_coverpoint(self.cg, "rloop") # R Wait self.cg.add_coverpoint("r_wait[0]", lambda: self.item.get("r_wait_cycles", idx=0, default=None)) self.cg._cps_["r_wait[0]"].set_comment("R WAIT CYCLES (1st beat)") for i in range(3): self.cg._cps_["r_wait[0]"].add_bin(f"{i}", i) self.cg._cps_["r_wait[0]"].add_bin("wait_cycles", range(0,256), stats=True)
[docs] def add_coverpoint(self, cg : avl.Covergroup, signal : str, signal_type : [Any]=None, comment : str=None) -> avl.Coverpoint: """ Wrapper around adding coverage point Lists are reduced to bit checks across all beats :param cg :param signal: Signal to add coverage point :param signal_type: Type of signal to add :returns: None """ if not hasattr(self.item, signal): return if comment is None: comment = signal.upper() attr_name = f"_{signal}_" if isinstance(getattr(self.item, signal), list) else signal var = getattr(self.item, attr_name) cg.add_coverpoint(signal, lambda: getattr(self.item, attr_name, None)) cg._cps_[signal].set_comment(comment) if isinstance(var, list): for i in range(var[0].width): cg._cps_[signal].add_bin(f"[{i}] == 0", lambda x, y=i : 0 == (x[1] & (1<<y))) cg._cps_[signal].add_bin(f"[{i}] == 1", lambda x, y=i : 0 != (x[0] & (1<<y))) elif signal_type is not None: for k,v in var.values().items(): cg._cps_[signal].add_bin(v, k) elif var.width <= 8: for i in range(2**var.width): cg._cps_[signal].add_bin(f"{i}", i) else: for i in range(var.width): cg._cps_[signal].add_bin(f"[{i}] == 0", lambda x, y=i : 0 == (x & (1<<y))) cg._cps_[signal].add_bin(f"[{i}] == 1", lambda x, y=i : 0 != (x & (1<<y))) return cg._cps_[signal]
[docs] def add_covercross(self, cg : avl.Covergroup, name : str, *args, comment : str=None) -> avl.Covercross: """ Wrapper around adding coverage cross :param cg :param str names of coverpoints :returns: None """ cps = [] for a in args: if a not in cg._cps_: return cps.append(cg._cps_[a]) cg.add_covercross(name, *cps)
[docs] async def run_phase(self) -> None: """ Run phase for the coverage component. """ while True: # Wait for an item to be available self.item = await self.item_port.blocking_get() # Sample self.cg.sample()
__all__ = ["Coverage"]