Variable Attributes
AVL provides similar functionality to the UVM field macros, without the unnecessary verbosity.
The is also avoids the long running arguments over whether the user should use the macros or not.
Only the most common attributes are supported, and the user is encouraged to use the Pythonic way of setting attributes.
Automatic inclusion in class comparison
Custom string formatting
Attributes can be:
Retrieved
Object.get_field_attributesCleared
Object.remove_field_attributes
Note
AVL implements no additional functionality to support copying.
If a user wishes to copy a class or variable the should follow the well understood Python copy library.
Naming Conventions
To keep things simple, all variables beginning with an underscore (“_”) are considered private and should not be accessed directly. Equally all private variables are excluded from all in-built support functions
This rule has been applied to all AVL base classes.
Printing / String Formatting
When using avl.Var variables, the user can set the string formatting of the variable as part of the class constructor.
For regualar Python variables the default string representation is used, unless a specific string format it registered.
By default all non-private class variables are included in the string representation of an avl.Object class.
AVL uses Tabulate to format the string representation of a class. By default the table is formatted using the “grid” format, but the user can customize.
However, not all tabulate table format support sub-tables nicely. The following formats are shown in the example below to work nicely and can be set by calling Object.set_table_fmt:
Format |
|---|
grid (default) |
fancy_grid |
simple_grid |
presto |
psql |
orgtbl |
rst |
jira |
# Copyright 2024 Apheleia
#
# Description:
# Apheleia attributes example
import avl
import cocotb
from collections import OrderedDict, defaultdict
class example_env(avl.Env):
def __init__(self, name, parent):
super().__init__(name, parent)
# Python variables
self.dec_var = 100
self.hex_var = 100
self.custom_var = 100
self.hex_list = [100, 200, 300]
self.dec_set = {100, 200, 300}
self.hex_dict = {"A": 100, "B": 200, "C": 300}
self.list_list = [[0,1], [2,3], [4,5]]
self.simple_dict = {"A": 1, "B": 2, "C": 3}
self.list_dict = {"X" : ["a", "b"], "Y" : [0]}
self.mixed = [{"dict" : ["value1", "value2"]}, ["list0", "list1", avl.Uint32(100, fmt=bin)]]
self.obj = avl.Object("obj", self)
self.tuple_var = (1, 2, 3)
self.dict_tuple = {"T1" : (10, 20), "T2" : (30, 40)}
self.dict_set = {"S1" : {1,2,3}, "S2" : {4,5,6}}
self.odict = OrderedDict({"one": 1, "two": 2, "three": 3})
self.ddict = defaultdict(list, {"first": [1,2], "second": [3,4]})
self.set_field_attributes("hex_var", fmt=hex)
self.set_field_attributes("hex_list", fmt=hex)
self.set_field_attributes("hex_dict", fmt=hex)
self.set_field_attributes("custom_var", fmt=self.custom_fmt)
def custom_fmt(self, value):
return f"custom_var={value}"
@cocotb.test
async def test(dut):
e = example_env("env", None)
for f in [e._table_fmt_, "fancy_grid", "presto", "simple_grid", "psql", "orgtbl", "rst", "jira"]:
print(f"\n\n{f}:\n\n")
e.set_table_fmt(fmt=f)
print(e)
# Stick to grid - show transpose
e.set_table_fmt(fmt="grid", transpose=True)
print(e)
# Show recurse
e.set_table_fmt(recurse=False)
print(e)
Expected output (grid format):
+-------------+----------------+
| name | env |
+-------------+----------------+
| dec_var | 100 |
+-------------+----------------+
| hex_var | 0x64 |
+-------------+----------------+
| custom_var | custom_var=100 |
+-------------+----------------+
| hex_list | 0x64 |
| | 0xc8 |
| | 0x12c |
+-------------+----------------+
| hex_dict | A: 0x64 |
| | B: 0xc8 |
| | C: 0x12c |
+-------------+----------------+
| list_list | - |
| | 0 |
| | 1 |
| | - |
| | 2 |
| | 3 |
| | - |
| | 4 |
| | 5 |
+-------------+----------------+
| simple_dict | A: 1 |
| | B: 2 |
| | C: 3 |
+-------------+----------------+
| list_dict | X: |
| | a |
| | b |
| | Y: |
| | 0 |
+-------------+----------------+
| mixed | - |
| | dict: |
| | value1 |
| | value2 |
| | - |
| | list0 |
| | list1 |
| | 0b1100100 |
+-------------+----------------+
| obj | +------+-----+ |
| | | name | obj | |
| | +------+-----+ |
+-------------+----------------+
Transposing
Depending on the class contents to may be useful to transpose the table representation of the class. This can be done by setting the transpose argument to True when calling Object.set_table_fmt.
Expected output (grid format):
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+----------------+
| name | dec_var | hex_var | custom_var | hex_list | hex_dict | list_list | simple_dict | list_dict | mixed | obj |
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+----------------+
| env | 100 | 0x64 | custom_var=100 | 0x64 | A: 0x64 | - | A: 1 | X: | - | +------+-----+ |
| | | | | 0xc8 | B: 0xc8 | 0 | B: 2 | a | dict: | | name | obj | |
| | | | | 0x12c | C: 0x12c | 1 | C: 3 | b | value1 | +------+-----+ |
| | | | | | | - | | Y: | value2 | |
| | | | | | | 2 | | 0 | - | |
| | | | | | | 3 | | | list0 | |
| | | | | | | - | | | list1 | |
| | | | | | | 4 | | | 0b1100100 | |
| | | | | | | 5 | | | | |
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+----------------+
Recursion
AVL supports recursive printing of classes. This is useful when the class contains nested classes or variables. However, this can lead to very large output, so it can be dictated by setting the recurse argument to True when calling Object.set_table_fmt.
Expected output (grid format):
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+--------------------------------+
| name | dec_var | hex_var | custom_var | hex_list | hex_dict | list_list | simple_dict | list_dict | mixed | obj |
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+--------------------------------+
| env | 100 | 0x64 | custom_var=100 | 0x64 | A: 0x64 | - | A: 1 | X: | - | type(Object) at 0x7a13c9fd3440 |
| | | | | 0xc8 | B: 0xc8 | 0 | B: 2 | a | dict: | |
| | | | | 0x12c | C: 0x12c | 1 | C: 3 | b | value1 | |
| | | | | | | - | | Y: | value2 | |
| | | | | | | 2 | | 0 | - | |
| | | | | | | 3 | | | list0 | |
| | | | | | | - | | | list1 | |
| | | | | | | 4 | | | 0b1100100 | |
| | | | | | | 5 | | | | |
+------+---------+---------+----------------+----------+----------+-----------+-------------+-----------+-------------+--------------------------------+
Comparison
By default all non-private class variables are included in the comparison of two avl.Object classes.
In addition a bi-directional comparison is optionally performed (on by default). This ensures that all fields in both classes are compared i.e. if a field is missing from either class the comparison will fail.
# Copyright 2024 Apheleia
#
# Description:
# Apheleia attributes example
import avl
import cocotb
class a(avl.Object):
def __init__(self, name, parent):
super().__init__(name, parent)
self.var_a = 0
self.var_b = 1
self.set_field_attributes("var_a", compare=False)
class example_env(avl.Env):
def __init__(self, name, parent):
super().__init__(name, parent)
self.a0 = a("a", self)
self.a1 = a("a", self)
self.a1.var_a = 1 # Should generate an error if not for attribute
assert self.a0.compare(self.a1, verbose=True)
@cocotb.test
async def test(dut):
example_env("env", None)
Expected output:
0.00ns INFO cocotb.regression running test (1/1)
0.0ns INFO None Field "name" comparison passed (a == a)
0.0ns INFO None Field "var_b" comparison passed (1 == 1)
0.0ns INFO None Field "name" comparison passed (a == a)
0.0ns INFO None Field "var_b" comparison passed (1 == 1)
0.00ns INFO cocotb.regression test passed