Skip to content
RTL Design Sherpa CocoTB Framework · Verification Infrastructure for RTL Testing
GitHub · Documentation Index · MIT License

gaxi_packet.py

GAXI Packet class with minimal protocol-specific extensions to the base Packet class. Provides GAXI-specific randomizer handling while inheriting all field management, masking, pack/unpack, and formatting functionality.

Overview

The GAXIPacket class provides: - Minimal extension of base Packet class for GAXI protocol - GAXI-specific randomizer handling for master and slave interfaces - Inherited functionality from base Packet for all field operations - Timing delay management for valid and ready signals - Protocol-specific delay calculations using FlexRandomizer

All field management, masking, pack/unpack, and formatting functionality is inherited from the base Packet class without modification.

Class

GAXIPacket

class GAXIPacket(Packet):
    def __init__(self, field_config=None, master_randomizer=None,
                 slave_randomizer=None, **kwargs)

Parameters: - field_config: Field configuration (FieldConfig object or dict) - master_randomizer: Optional randomizer for master interface timing - slave_randomizer: Optional randomizer for slave interface timing
- **kwargs: Initial field values passed to parent Packet class

Inherited from Packet: - All field access via attributes (e.g., packet.addr, packet.data) - Automatic field validation and masking - FIFO packing/unpacking support - Rich formatting and comparison capabilities - Thread-safe field caching

GAXI-Specific Methods

Randomizer Management

set_master_randomizer(randomizer)

Set the master randomizer for valid delay generation.

Parameters: - randomizer: FlexRandomizer instance for master timing

from CocoTBFramework.shared.flex_randomizer import FlexRandomizer

# Create master randomizer
master_randomizer = FlexRandomizer({
    'valid_delay': ([(0, 0), (1, 5), (10, 20)], [0.6, 0.3, 0.1])
})

packet.set_master_randomizer(master_randomizer)

set_slave_randomizer(randomizer)

Set the slave randomizer for ready delay generation.

Parameters: - randomizer: FlexRandomizer instance for slave timing

# Create slave randomizer
slave_randomizer = FlexRandomizer({
    'ready_delay': ([(0, 1), (2, 8), (9, 30)], [0.5, 0.3, 0.2])
})

packet.set_slave_randomizer(slave_randomizer)

Delay Generation

get_master_delay()

Get the delay for the master interface (valid delay).

Returns: Delay in cycles (0 if no randomizer)

# Get valid delay for master
valid_delay = packet.get_master_delay()
print(f"Master should wait {valid_delay} cycles before asserting valid")

get_slave_delay()

Get the delay for the slave interface (ready delay).

Returns: Delay in cycles (0 if no randomizer)

# Get ready delay for slave
ready_delay = packet.get_slave_delay()
print(f"Slave should wait {ready_delay} cycles before asserting ready")

Usage Patterns

Basic Packet Creation

from CocoTBFramework.components.gaxi import GAXIPacket
from CocoTBFramework.shared.field_config import FieldConfig

# Create field configuration
field_config = FieldConfig()
field_config.add_field(FieldDefinition("addr", 32, format="hex"))
field_config.add_field(FieldDefinition("data", 32, format="hex"))
field_config.add_field(FieldDefinition("cmd", 4, format="hex"))

# Create packet with initial values
packet = GAXIPacket(
    field_config=field_config,
    addr=0x1000,
    data=0xDEADBEEF,
    cmd=0x2  # WRITE command
)

# Access fields directly (inherited from base Packet)
print(f"Address: 0x{packet.addr:X}")
print(f"Data: 0x{packet.data:X}")
print(f"Command: {packet.cmd}")

Packet with Timing Randomizers

from CocoTBFramework.shared.flex_randomizer import FlexRandomizer

# Create randomizers for timing
master_randomizer = FlexRandomizer({
    'valid_delay': ([(0, 0), (1, 3), (5, 10)], [0.7, 0.2, 0.1])
})

slave_randomizer = FlexRandomizer({
    'ready_delay': ([(0, 0), (1, 5)], [0.8, 0.2])
})

# Create packet with randomizers
packet = GAXIPacket(
    field_config=field_config,
    master_randomizer=master_randomizer,
    slave_randomizer=slave_randomizer,
    addr=0x2000,
    data=0xCAFEBABE
)

# Get timing delays
valid_delay = packet.get_master_delay()
ready_delay = packet.get_slave_delay()

print(f"Master valid delay: {valid_delay} cycles")
print(f"Slave ready delay: {ready_delay} cycles")

Field Operations (Inherited)

# All field operations inherited from base Packet class

# Field validation and masking (automatic)
packet.addr = 0x123456789  # Automatically masked to 32 bits -> 0x23456789
packet.data = 0xDEADBEEF   # Valid 32-bit value

# FIFO packing/unpacking (inherited)
fifo_data = packet.pack_for_fifo()
print(f"FIFO data: {fifo_data}")

# Create new packet from FIFO data
new_packet = GAXIPacket(field_config)
new_packet.unpack_from_fifo(fifo_data)

# Formatting (inherited)
print(packet.formatted())          # Detailed format
print(packet.formatted(compact=True))  # Compact format

Transaction Timing Integration

import cocotb
from cocotb.triggers import RisingEdge, Timer

class TimedGAXIMaster:
    def __init__(self, dut, clock, field_config):
        self.dut = dut
        self.clock = clock
        self.field_config = field_config

        # Create timing randomizers
        self.master_randomizer = FlexRandomizer({
            'valid_delay': ([(0, 0), (1, 5)], [0.8, 0.2])
        })

    async def send_packet(self, **field_values):
        """Send packet with randomized timing"""
        # Create packet with randomizer
        packet = GAXIPacket(
            field_config=self.field_config,
            master_randomizer=self.master_randomizer,
            **field_values
        )

        # Get timing delay
        valid_delay = packet.get_master_delay()

        # Apply delay before driving
        if valid_delay > 0:
            await Timer(valid_delay, units='clk')

        # Drive packet fields
        self.dut.addr.value = packet.addr
        self.dut.data.value = packet.data
        self.dut.valid.value = 1

        # Wait for handshake
        while self.dut.ready.value != 1:
            await RisingEdge(self.clock)

        await RisingEdge(self.clock)
        self.dut.valid.value = 0

        return packet

# Usage
master = TimedGAXIMaster(dut, clock, field_config)
packet = await master.send_packet(addr=0x1000, data=0x12345678, cmd=0x2)

Slave Timing Integration

class TimedGAXISlave:
    def __init__(self, dut, clock, field_config):
        self.dut = dut
        self.clock = clock
        self.field_config = field_config

        # Create slave timing randomizer
        self.slave_randomizer = FlexRandomizer({
            'ready_delay': ([(0, 0), (1, 3), (5, 10)], [0.6, 0.3, 0.1])
        })

    async def receive_packet(self):
        """Receive packet with randomized ready timing"""
        # Wait for valid
        while self.dut.valid.value != 1:
            await RisingEdge(self.clock)

        # Create packet for timing
        packet = GAXIPacket(
            field_config=self.field_config,
            slave_randomizer=self.slave_randomizer
        )

        # Get ready delay
        ready_delay = packet.get_slave_delay()

        # Apply delay before asserting ready
        if ready_delay > 0:
            await Timer(ready_delay, units='clk')

        # Assert ready and capture data
        self.dut.ready.value = 1
        await RisingEdge(self.clock)

        # Capture packet data
        packet.addr = int(self.dut.addr.value)
        packet.data = int(self.dut.data.value)
        if hasattr(self.dut, 'cmd'):
            packet.cmd = int(self.dut.cmd.value)

        # Deassert ready
        self.dut.ready.value = 0

        return packet

# Usage
slave = TimedGAXISlave(dut, clock, field_config)
received_packet = await slave.receive_packet()

Batch Packet Creation

def create_test_packets(field_config, count=10):
    """Create a batch of test packets with varied data"""
    packets = []

    # Create common randomizers
    master_randomizer = FlexRandomizer({
        'valid_delay': ([(0, 0), (1, 2)], [0.9, 0.1])
    })

    slave_randomizer = FlexRandomizer({
        'ready_delay': ([(0, 1), (2, 5)], [0.7, 0.3])
    })

    for i in range(count):
        packet = GAXIPacket(
            field_config=field_config,
            master_randomizer=master_randomizer,
            slave_randomizer=slave_randomizer,
            addr=0x1000 + i*4,
            data=0x10000000 + i,
            cmd=0x2 if i % 2 == 0 else 0x1  # Alternate READ/write
        )
        packets.append(packet)

    return packets

# Usage
test_packets = create_test_packets(field_config, count=20)
for i, packet in enumerate(test_packets):
    print(f"Packet {i}: {packet.formatted(compact=True)}")
    print(f"  Master delay: {packet.get_master_delay()}")
    print(f"  Slave delay: {packet.get_slave_delay()}")

Packet Comparison and Validation

def validate_packet_sequence(sent_packets, received_packets):
    """Validate that received packets match sent packets"""
    assert len(sent_packets) == len(received_packets), \
        f"Packet count mismatch: sent {len(sent_packets)}, received {len(received_packets)}"

    for i, (sent, received) in enumerate(zip(sent_packets, received_packets)):
        # Use inherited comparison (ignores timing fields)
        if sent != received:
            print(f"Packet {i} mismatch:")
            print(f"  Sent:     {sent.formatted(compact=True)}")
            print(f"  Received: {received.formatted(compact=True)}")
            assert False, f"Packet {i} data mismatch"
        else:
            print(f"Packet {i}: ✓ Match")

    print(f"All {len(sent_packets)} packets validated successfully")

# Usage
sent_packets = create_test_packets(field_config, 10)
# ... send packets through DUT ...
received_packets = capture_received_packets()
validate_packet_sequence(sent_packets, received_packets)

Multi-Field Packets

# Create complex field configuration
complex_field_config = FieldConfig()
complex_field_config.add_field(FieldDefinition("addr", 32, format="hex"))
complex_field_config.add_field(FieldDefinition("data", 64, format="hex"))
complex_field_config.add_field(FieldDefinition("cmd", 4, format="hex"))
complex_field_config.add_field(FieldDefinition("id", 8, format="hex"))
complex_field_config.add_field(FieldDefinition("size", 3, format="hex"))

# Create packet with all fields
complex_packet = GAXIPacket(
    field_config=complex_field_config,
    addr=0x12345678,
    data=0xDEADBEEFCAFEBABE,
    cmd=0x3,
    id=0x55,
    size=0x4
)

# All field operations work automatically
print(f"Total packet bits: {complex_packet.get_total_bits()}")
print(f"Formatted packet:\n{complex_packet.formatted()}")

# FIFO operations handle all fields
fifo_data = complex_packet.pack_for_fifo()
print(f"FIFO data: {fifo_data}")

Randomizer Delay Caching

def test_delay_caching():
    """Test that delays are cached until randomizer is reset"""
    packet = GAXIPacket(field_config)

    # Set randomizer
    randomizer = FlexRandomizer({
        'valid_delay': ([(1, 5)], [1.0])
    })
    packet.set_master_randomizer(randomizer)

    # First call generates and caches delay
    delay1 = packet.get_master_delay()

    # Subsequent calls return cached value
    delay2 = packet.get_master_delay()
    delay3 = packet.get_master_delay()

    assert delay1 == delay2 == delay3, "Delay should be cached"
    print(f"Cached delay: {delay1} cycles")

    # Setting new randomizer resets cache
    new_randomizer = FlexRandomizer({
        'valid_delay': ([(10, 20)], [1.0])
    })
    packet.set_master_randomizer(new_randomizer)

    # New delay generated
    new_delay = packet.get_master_delay()
    print(f"New delay after randomizer change: {new_delay} cycles")

    # This new delay is now cached
    cached_new_delay = packet.get_master_delay()
    assert new_delay == cached_new_delay, "New delay should be cached"

test_delay_caching()

Error Handling

Field Validation Errors

# Field validation handled by base Packet class
try:
    packet = GAXIPacket(field_config, addr="invalid")
except ValueError as e:
    print(f"Invalid field value: {e}")

Missing Randomizers

# Graceful handling when randomizers not set
packet = GAXIPacket(field_config)

# Returns 0 when no randomizer
master_delay = packet.get_master_delay()  # Returns 0
slave_delay = packet.get_slave_delay()    # Returns 0

print(f"Delays without randomizers: master={master_delay}, slave={slave_delay}")

Randomizer Errors

# Handle randomizer generation errors
try:
    delay = packet.get_master_delay()
except Exception as e:
    print(f"Randomizer error: {e}")
    delay = 0  # Use default delay

Best Practices

1. Use Appropriate Randomizers for Each Interface

# Master randomizer for valid delays
master_randomizer = FlexRandomizer({'valid_delay': ([(0, 5)], [1.0])})

# Slave randomizer for ready delays  
slave_randomizer = FlexRandomizer({'ready_delay': ([(0, 3)], [1.0])})

packet = GAXIPacket(field_config, 
                   master_randomizer=master_randomizer,
                   slave_randomizer=slave_randomizer)

2. Leverage Inherited Functionality

# Use inherited field operations
packet.addr = 0x1000           # Field validation automatic
fifo_data = packet.pack_for_fifo()  # FIFO conversion automatic
formatted = packet.formatted()      # Rich formatting automatic

3. Cache Timing Delays Appropriately

# Delays are cached per packet - appropriate for single transaction
master_delay = packet.get_master_delay()  # Generated and cached
same_delay = packet.get_master_delay()    # Returns cached value

# Create new packet for new transaction with fresh delays
new_packet = GAXIPacket(field_config, master_randomizer=randomizer)
new_delay = new_packet.get_master_delay()  # Fresh delay

4. Use Timing Integration in Components

# Integrate timing into master/slave components
class TimingAwareMaster:
    def create_packet(self, **fields):
        return GAXIPacket(self.field_config, 
                         master_randomizer=self.randomizer,
                         **fields)

    async def send(self, packet):
        delay = packet.get_master_delay()
        # Apply delay and send

5. Validate Packet Integrity

# Use inherited comparison for validation
assert sent_packet == received_packet, "Packet data mismatch"

# Use inherited formatting for debugging
if sent_packet != received_packet:
    print(f"Sent: {sent_packet.formatted(compact=True)}")
    print(f"Received: {received_packet.formatted(compact=True)}")

The GAXIPacket provides a minimal, focused extension to the base Packet class specifically for GAXI protocol timing requirements while leveraging all the robust field management, validation, and formatting capabilities of the base packet infrastructure.