|
CocoTB Framework · Verification Infrastructure for RTL Testing GitHub · Documentation Index · MIT License |
field_config.py¶
Field Configuration Classes for GAXI Validation Framework that provide robust and type-safe field definitions, replacing dictionary-based approaches with proper class structures.
Overview¶
The field_config.py module provides classes for defining field configurations with comprehensive validation, encoding support, and Rich table formatting. It supports both individual field definitions and complete packet configurations.
Classes¶
FieldDefinition¶
Definition of a single field within a packet using a dataclass structure.
Constructor¶
@dataclass
class FieldDefinition:
name: str
bits: int
default: int = 0
format: str = "hex"
display_width: int = 0
active_bits: Optional[Tuple[int, int]] = None
description: Optional[str] = None
encoding: Optional[Dict[int, str]] = None
Parameters:
- name: Field name
- bits: Width of the field in bits
- default: Default value for the field (default: 0)
- format: Display format - 'hex', 'bin', or 'dec' (default: 'hex')
- display_width: Width for display formatting (auto-calculated if 0)
- active_bits: Tuple of (msb, lsb) defining active bit range (defaults to full width)
- description: Human-readable description (defaults to formatted name)
- encoding: Optional dictionary mapping values to state names
Methods¶
to_dict() -> Dict[str, Any]¶
Convert to dictionary format for backward compatibility.
field_def = FieldDefinition("addr", 32, format="hex", description="Address field")
field_dict = field_def.to_dict()
# Returns: {'bits': 32, 'default': 0, 'format': 'hex', 'display_width': 8, ...}
from_dict(name: str, field_dict: Dict[str, Any]) -> 'FieldDefinition'¶
Create a FieldDefinition from a dictionary representation.
field_dict = {'bits': 16, 'format': 'bin', 'default': 0xFF}
field_def = FieldDefinition.from_dict("data", field_dict)
Examples¶
# Basic field definition
addr_field = FieldDefinition(
name="addr",
bits=32,
format="hex",
description="Memory address"
)
# Field with active bits (partial field)
status_field = FieldDefinition(
name="status",
bits=8,
active_bits=(7, 4), # Only bits 7:4 are used
format="bin",
encoding={
0x0: "IDLE",
0x1: "ACTIVE",
0x2: "ERROR",
0x3: "DONE"
}
)
# Binary field with custom width
mask_field = FieldDefinition(
name="mask",
bits=16,
format="bin",
display_width=16, # Show all 16 bits
default=0xFFFF
)
FieldConfig¶
Configuration of all fields in a packet, maintaining field order and providing helper methods for field manipulation.
Constructor¶
Creates an empty field configuration. Fields are added using the add_field() method.
Methods¶
add_field(field_def: FieldDefinition) -> 'FieldConfig'¶
Add a field to the configuration.
Parameters:
- field_def: Field definition to add
Returns: Self for method chaining
config = FieldConfig()
config.add_field(FieldDefinition("addr", 32, format="hex"))
config.add_field(FieldDefinition("data", 32, format="hex"))
add_field_dict(name: str, field_dict: Dict[str, Any]) -> 'FieldConfig'¶
Add a field from dictionary representation (for backward compatibility).
remove_field(name: str) -> 'FieldConfig'¶
Remove a field from the configuration.
get_field(name: str) -> FieldDefinition¶
Get a field definition by name.
Raises: KeyError if field doesn't exist
has_field(name: str) -> bool¶
Check if a field exists in the configuration.
field_names() -> List[str]¶
Get ordered list of field names.
fields() -> List[FieldDefinition]¶
Get ordered list of field definitions.
items()¶
Get name/definition pairs in order, similar to dict.items().
for name, field_def in config.items():
print(f"{name} = {field_def.bits} bits, format={field_def.format}")
get_total_bits() -> int¶
Calculate the total number of bits across all fields.
create_packet()¶
Create a packet object compatible with the field configuration.
debug_str(indent=0) -> str¶
Return a formatted string representation using Rich tables.
Dictionary-like Interface¶
FieldConfig supports dictionary-like operations:
# Length
num_fields = len(config)
# Iteration
for field_name in config:
print(field_name)
# Membership testing
if "addr" in config:
print("Address field exists")
# Direct access
addr_field = config["addr"]
Validation and Creation Methods¶
validate_and_create(field_dict: Dict[str, Dict[str, Any]], raise_errors: bool = False) -> 'FieldConfig'¶
Validate a dictionary-based field configuration and convert to FieldConfig object.
Parameters:
- field_dict: Dictionary mapping field names to field properties
- raise_errors: If True, raise exceptions for validation errors; if False, attempt to correct or warn
Returns: New validated FieldConfig instance
# Define fields as dictionary
field_dict = {
'addr': {'bits': 32, 'format': 'hex'},
'data': {'bits': 32, 'format': 'hex'},
'ctrl': {'bits': 8, 'format': 'bin', 'encoding': {0: 'READ', 1: 'WRITE'}}
}
# Validate and create
config = FieldConfig.validate_and_create(field_dict)
from_dict(field_dict: Dict[str, Dict[str, Any]]) -> 'FieldConfig'¶
Create a FieldConfig from a dictionary representation.
Pre-built Configuration Methods¶
create_data_only(data_width: int = 32) -> 'FieldConfig'¶
Create a simple data-only field configuration.
create_standard(addr_width: int = 32, data_width: int = 32) -> 'FieldConfig'¶
Create a standard address/data field configuration.
create_multi_data(addr_width: int = 4, ctrl_width: int = 4, data_width: int = 8, num_data: int = 2) -> 'FieldConfig'¶
Create a multi-data field configuration with addr, ctrl, and multiple data fields.
# Creates: addr(4), ctrl(4), data0(8), data1(8)
config = FieldConfig.create_multi_data(addr_width=4, ctrl_width=4, data_width=8, num_data=2)
Field Manipulation Methods¶
update_field_width(field_name: str, new_bits: int, update_active_bits: bool = True) -> 'FieldConfig'¶
Update a field's bit width and optionally its active bits.
set_encoding(field_name: str, encoding: Dict[int, str]) -> 'FieldConfig'¶
Set an encoding dictionary for a field.
add_encoding_value(field_name: str, value: int, state_name: str) -> 'FieldConfig'¶
Add a single encoding value to a field.
Usage Patterns¶
Basic Field Configuration¶
# Create configuration
config = FieldConfig()
# Add address field
config.add_field(FieldDefinition(
name="addr",
bits=32,
format="hex",
description="Memory address"
))
# Add data field
config.add_field(FieldDefinition(
name="data",
bits=32,
format="hex",
description="Data value"
))
# Add control field with encoding
config.add_field(FieldDefinition(
name="cmd",
bits=4,
format="hex",
encoding={
0x0: "NOP",
0x1: "READ",
0x2: "WRITE",
0x3: "BURST"
},
description="Command type"
))
print(f"Total packet size: {config.get_total_bits()} bits")
Configuration with Active Bits¶
# Field where only certain bits are used
config.add_field(FieldDefinition(
name="status",
bits=32,
active_bits=(15, 8), # Only bits 15:8 are active
format="hex",
description="Status register"
))
# This is useful for registers where not all bits are used
Validation and Error Correction¶
# Dictionary with potential issues
problematic_config = {
'addr': {'bits': 32, 'format': 'hex'},
'data': {'bits': -5, 'format': 'invalid'}, # Invalid bits and format
'ctrl': {} # Missing required 'bits' field
}
# Validate and auto-correct
try:
config = FieldConfig.validate_and_create(problematic_config, raise_errors=False)
print("Configuration created with corrections")
except ValueError as e:
print(f"Validation failed: {e}")
Dynamic Field Configuration¶
class ConfigBuilder:
def __init__(self):
self.config = FieldConfig()
def add_address_field(self, width=32):
self.config.add_field(FieldDefinition("addr", width, format="hex"))
return self
def add_data_fields(self, count=1, width=32):
for i in range(count):
self.config.add_field(FieldDefinition(f"data{i}", width, format="hex"))
return self
def add_control_field(self, width=8, commands=None):
encoding = commands or {0: "NOP", 1: "READ", 2: "WRITE"}
self.config.add_field(FieldDefinition("ctrl", width, format="bin", encoding=encoding))
return self
def build(self):
return self.config
# Usage
config = (ConfigBuilder()
.add_address_field(32)
.add_data_fields(count=2, width=64)
.add_control_field(width=4)
.build())
Integration with Packets¶
# Create configuration
config = FieldConfig.create_standard(addr_width=32, data_width=32)
# Create packet using configuration
packet = config.create_packet()
# Set field values
packet.addr = 0x12345678
packet.data = 0xDEADBEEF
# Access field configuration
addr_field = config.get_field("addr")
print(f"Address field: {addr_field.bits} bits, format: {addr_field.format}")
Rich Table Display¶
# Display configuration in formatted table
print(config.debug_str())
# Output:
# ┏━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
# ┃ Field Name ┃ Bits ┃ Format ┃ Active Bits ┃ Default ┃ Encoding ┃ Description ┃
# ┡━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
# │ addr │ 32 │ hex │ (31:0) │ 0x0 │ │ Address │
# │ data │ 32 │ hex │ (31:0) │ 0x0 │ │ Data value │
# └────────────┴──────┴────────┴─────────────┴─────────┴──────────┴────────────────────┘
# Total bits: 64
Converting Legacy Configurations¶
# Legacy dictionary format
legacy_config = {
'addr': {
'bits': 32,
'format': 'hex',
'default': 0,
'description': 'Address field'
},
'data': {
'bits': 32,
'format': 'hex',
'default': 0,
'description': 'Data field'
}
}
# Convert to new format
modern_config = FieldConfig.from_dict(legacy_config)
# Or with validation
validated_config = FieldConfig.validate_and_create(legacy_config)
Error Handling¶
The FieldConfig system includes comprehensive error handling:
Field Definition Validation¶
- Bit Width: Must be positive integer
- Active Bits: Must be within field bounds
- Format: Must be 'hex', 'bin', or 'dec'
- Encoding: Keys must be integers, values must be strings
Configuration Validation¶
- Duplicate Fields: Prevents adding fields with same name
- Missing Fields: Graceful handling of missing field lookups
- Type Checking: Ensures proper types for all parameters
Error Messages¶
try:
config.add_field(FieldDefinition("test", bits=-5))
except ValueError as e:
# Error: Invalid active_bits (4:-1) for field 'test' with width -5
pass
try:
config.get_field("nonexistent")
except KeyError as e:
# Error: Field 'nonexistent' not found in configuration
pass
Best Practices¶
- Use Method Chaining: Take advantage of fluent interface for building configurations
- Validate Early: Use
validate_and_create()for external configurations - Document Fields: Always provide meaningful descriptions
- Use Encoding: Define state names for control fields
- Check Existence: Use
has_field()before accessing optional fields - Display Configs: Use
debug_str()for configuration review and documentation
Integration with Other Components¶
FieldConfig integrates seamlessly with other framework components:
- Packet: Provides field structure and validation
- PacketFactory: Uses FieldConfig for packet creation
- DataStrategies: Uses field information for efficient collection/driving
- FlexRandomizer: Can generate values based on field bit widths
- Memory Model: Uses field definitions for transaction processing