AVL Factory

Inheritance diagram of avl._core.factory

Overview

The AVL factory provides the same functionality as the UVM factory without the complexity of the class::type_id::create() syntax as the factory is native to the avl.Object class.

All classes that exend the avl.Object class can be overridden by the factory.

Debugging the factory can be done by printing via either the Factory.print_factory method, or simply casting the Factory() to a string.

Override by Type

# Copyright 2024 Apheleia
#
# Description:
# Apheleia factory example


import avl
import cocotb


class example_env(avl.Env):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.s = sub_comp_A("s", self)


class sub_comp_A(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.error("sub_comp_A should have been overriden by sub_comp_B")


class sub_comp_B(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.info("sub_comp_B successfully overriden sub_comp_A")


@cocotb.test
async def test(dut):
    avl.Factory.set_override_by_type(sub_comp_A, sub_comp_B)
    e = example_env("env", None)
    await e.start()

Override by Instance

There is no override by name in AVL. Instead, the factory can be used to override by instance. As wildcards (see below) are supported names can always be found.

# Copyright 2024 Apheleia
#
# Description:
# Apheleia factory example


import avl
import cocotb


class example_env(avl.Env):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.s = sub_comp_A("s", self)
        self.o = object_A("o", self)


class sub_comp_A(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.error("sub_comp_A should have been overriden by sub_comp_B")


class sub_comp_B(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.info("sub_comp_B successfully overriden sub_comp_A")


class object_A(avl.Object):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.error("object_A should have been overriden by object_B")


class object_B(avl.Object):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.info("object_B successfully overriden object_A")


@cocotb.test
async def test(dut):
    avl.Factory.set_override_by_instance("env.s", sub_comp_B)
    avl.Factory.set_override_by_instance("env.o", object_B)

    e = example_env("env", None)
    await e.start()

Wildcards are also supported. These are implemented using the Python fnmatch module. If multiple matches are found, the most specific match is used Factory.specificity.

# Copyright 2024 Apheleia
#
# Description:
# Apheleia factory example


import avl
import cocotb


class example_env(avl.Env):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.s = sub_comp_A("super_long_name", self)


class sub_comp_A(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.error("sub_comp_A should have been overriden by sub_comp_B")


class sub_comp_B(avl.Component):
    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.info("sub_comp_B successfully overriden sub_comp_A")


@cocotb.test
async def test0(dut):
    avl.Factory.set_override_by_instance("env.super_long_name", sub_comp_B)
    avl.Factory.set_override_by_instance("env.*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

@cocotb.test
async def test1(dut):
    avl.Factory.set_override_by_instance("env.super_*", sub_comp_B)
    avl.Factory.set_override_by_instance("*.super_l*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

@cocotb.test
async def test2(dut):
    avl.Factory.set_override_by_instance("env.super_*", sub_comp_B)
    avl.Factory.set_override_by_instance("env.super_??*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

@cocotb.test
async def test3(dut):
    avl.Factory.set_override_by_instance("env.super_long_nam?", sub_comp_B)
    avl.Factory.set_override_by_instance("env.super_long_nam*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

@cocotb.test
async def test4(dut):
    avl.Factory.set_override_by_instance("env.super_[a-z]", sub_comp_B)
    avl.Factory.set_override_by_instance("env.super_*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

@cocotb.test
async def test5(dut):
    avl.Factory.set_override_by_instance("en*.super_[a-z]", sub_comp_B)
    avl.Factory.set_override_by_instance("[a-z].super_*", sub_comp_A) # Shouldn't be used as not the closest match

    e = example_env("env", None)
    await e.start()

Variables

Similar to the UVM config_db / resource_db variables can be set and retrieved from the factory. No copying is done automatically, classes are passed by reference and literals by value.

The Factory.set_variable method adds a variable to the factory at a path. Wildcards are allowed in the path. When the Factory.get_variable method is called, it will attempt to match the provided path to the existing paths in the factory. If a match is found, it will return the associated value. If multiple matches are found, the Factory will use the Factory.specificity method on the matching paths to determine which path is the most specific, then it will return the value associated with that path. Otherwise, it will return a default value. If no default value is provided, it will raise a KeyError.

The Factory can be useful for sharing avl.Var objects, cocotb hdl handles, and configurations for components.

# Copyright 2024 Apheleia
#
# Description:
# Apheleia factory example


import avl
import cocotb


class example_env(avl.Env):
    def __init__(self, name, parent):
        super().__init__(name, parent)

        # Get variable env.a in factory (set in the test method)
        self.a = avl.Factory.get_variable(f"{self.get_full_name()}.a")

        if self.a != 100:
            self.error(f"Expected a to be 100, got {self.a}")

        # Get variable env.b from the factory. Because it was never set, use the default
        # value 200 instead.
        self.b = avl.Factory.get_variable(f"{self.get_full_name()}.b", default=200)

        if self.b != 200:
            self.error(f"Expected b to be 200, got {self.b}")

        # Example of allow_override
        self.c = avl.Factory.get_variable(f"{self.get_full_name()}.c", default=300)

        if self.c != 300:
            self.error(f"Expected c to be 300, got {self.c}")

@cocotb.test
async def test(dut):
    avl.Factory.set_variable("env.a", 100)
    avl.Factory.set_variable("env.a", 99) # Should be ignored

    avl.Factory.set_variable("env.c", 100)
    avl.Factory.set_variable("env.c", 300, allow_override=True) # Should be obeyed

    e = example_env("env", None)
    await e.start()