|
CocoTB Framework · Verification Infrastructure for RTL Testing GitHub · Documentation Index · MIT License |
apb_packet.py¶
APB Packet and Transaction classes providing object-oriented transaction handling with randomization support. This module extends the base Packet class with APB-specific functionality and includes transaction generators for test stimulus.
Overview¶
The apb_packet.py module provides two main classes:
- APBPacket: Protocol-specific packet extending the base Packet class
- APBTransaction: Randomized transaction generator for test stimulus
Key Features¶
- Object-oriented transaction handling with field validation
- Built-in randomization with configurable constraints
- Protocol compliance checking for APB specification
- Flexible field configuration with default APB layout
- Integration with base framework components
Constants and Mappings¶
Core Classes¶
APBPacket¶
APB-specific packet class that extends the base Packet with APB protocol fields and functionality.
Constructor¶
APBPacket(field_config=None, skip_compare_fields=None, data_width=32, addr_width=32, strb_width=4, **kwargs)
Parameters:
- field_config: Field configuration (default: auto-generated APB config)
- skip_compare_fields: Fields to skip during equality comparison
- data_width: Width of data fields in bits (default: 32)
- addr_width: Width of address field in bits (default: 32)
- strb_width: Width of strobe field in bits (default: 4)
- **kwargs: Initial field values (e.g., paddr=0x123, pwrite=1)
# Create APB packet with default configuration
packet = APBPacket(
pwrite=1, # Write transaction
paddr=0x1000, # Address
pwdata=0xDEADBEEF, # Write data
pstrb=0xF # All strobes active
)
# Create with custom configuration
custom_packet = APBPacket(
data_width=64,
addr_width=16,
strb_width=8,
pwrite=0, # Read transaction
paddr=0x200
)
Properties¶
direction: String representation of transaction direction ('READ' or 'WRITE')data_width: Data field width in bitsaddr_width: Address field width in bitsstrb_width: Strobe field width in bitscount: Transaction counter for identification
APB Field Configuration¶
The default APB field configuration includes:
| Field | Bits | Format | Description |
|---|---|---|---|
| pwrite | 1 | dec | Write Enable (0=Read, 1=Write) |
| paddr | 32 | hex | Address |
| pwdata | 32 | hex | Write Data |
| prdata | 32 | hex | Read Data |
| pstrb | 4 | bin | Write Strobes |
| pprot | 3 | hex | Protection Control |
| pslverr | 1 | dec | Slave Error |
Methods¶
create_apb_field_config(addr_width, data_width, strb_width) [Static]¶
Create default field configuration for APB packets.
Parameters:
- addr_width: Address field width in bits
- data_width: Data field width in bits
- strb_width: Strobe field width in bits
Returns: FieldConfig object with APB fields
# Create custom field configuration
config = APBPacket.create_apb_field_config(
addr_width=16,
data_width=64,
strb_width=8
)
# Use in packet creation
packet = APBPacket(field_config=config)
__str__() -> str¶
Detailed string representation showing all packet fields.
print(packet)
# Output:
# APB Packet:
# Direction: WRITE
# Address: 0x00001000
# Write Data: 0xDEADBEEF
# Strobes: 1111
# Protection: 0x0
# Slave Err: 0
# Start Time: 1000 ns
# End Time: 2000 ns
# Duration: 1000 ns
# Count: 1
formatted(compact=False) -> str¶
Return formatted string representation.
Parameters:
- compact: If True, return compact one-line format
# Detailed format
print(packet.formatted())
# Compact format
print(packet.formatted(compact=True))
# Output: APBPacket(time=1000, dir=WRITE, addr=0x00001000, wdata=0xDEADBEEF, strb=1111, prot=0x0)
__eq__(other) -> bool¶
Compare packets for equality with APB-specific logic.
packet1 = APBPacket(pwrite=1, paddr=0x100, pwdata=0x123)
packet2 = APBPacket(pwrite=1, paddr=0x100, pwdata=0x123)
assert packet1 == packet2 # True
# Direction-aware data comparison
read_packet = APBPacket(pwrite=0, paddr=0x100, prdata=0x456)
write_packet = APBPacket(pwrite=1, paddr=0x100, pwdata=0x123)
assert read_packet != write_packet # Different directions and data
APBTransaction¶
Randomized transaction generator that extends the Randomized base class for constraint-based stimulus generation.
Constructor¶
Parameters:
- data_width: Data width in bits (default: 32)
- addr_width: Address width in bits (default: 32)
- strb_width: Strobe width in bits (default: 4)
- randomizer: FlexRandomizer instance or constraint dictionary
- **kwargs: Initial field values
# Create with default randomization
transaction = APBTransaction()
# Create with custom randomization
custom_randomizer = FlexRandomizer({
'pwrite': ([(0, 0), (1, 1)], [1, 2]), # 2:1 write bias
'paddr': ([(0x1000, 0x1FFF)], [1]), # Limited address range
'pstrb': ([(0xF, 0xF)], [1]) # Always full strobes
})
transaction = APBTransaction(
randomizer=custom_randomizer,
data_width=64
)
Default Randomization¶
The default randomizer provides realistic APB traffic patterns:
# Default randomization constraints
{
'pwrite': ([(0, 0), (1, 1)], [1, 1]), # Equal read/write
'paddr': ([(0, addr_min), (addr_max_lo, addr_max_hi)], [4, 1]), # Address distribution
'pstrb': ([(15, 15), (0, 14)], [4, 1]), # Mostly full strobes
'pprot': ([(0, 0), (1, 7)], [4, 1]) # Mostly normal protection
}
Methods¶
next() -> APBPacket¶
Generate the next randomized transaction packet.
Returns: APBPacket with randomized field values
# Generate random transactions
for i in range(10):
packet = transaction.next()
print(f"Transaction {i}: {packet.formatted(compact=True)}")
set_constrained_random() -> 'APBTransaction'¶
Set fields using constrained randomization.
Returns: Self for method chaining
formatted(compact=False) -> str¶
Return formatted representation of the transaction.
Usage Patterns¶
Basic Packet Creation¶
from CocoTBFramework.components.apb.apb_packet import APBPacket
# Create write packet
write_packet = APBPacket(
pwrite=1,
paddr=0x2000,
pwdata=0x12345678,
pstrb=0xF,
pprot=0
)
# Create read packet
read_packet = APBPacket(
pwrite=0,
paddr=0x2000,
pprot=0
)
print(f"Write: {write_packet.formatted(compact=True)}")
print(f"Read: {read_packet.formatted(compact=True)}")
Custom Field Configuration¶
# Create 64-bit APB configuration
config = APBPacket.create_apb_field_config(
addr_width=32,
data_width=64,
strb_width=8
)
# Create packet with custom configuration
packet = APBPacket(
field_config=config,
pwrite=1,
paddr=0x8000,
pwdata=0x123456789ABCDEF0,
pstrb=0xFF # All 8 strobes active
)
Transaction Generation¶
from CocoTBFramework.components.apb.apb_packet import APBTransaction
# Create transaction generator
transaction = APBTransaction(
data_width=32,
addr_width=16
)
# Generate test sequence
packets = []
for i in range(100):
packet = transaction.next()
packets.append(packet)
# Analyze generated patterns
write_count = sum(1 for p in packets if p.direction == 'WRITE')
read_count = len(packets) - write_count
print(f"Generated {write_count} writes, {read_count} reads")
Constrained Randomization¶
# Create address-constrained randomizer
address_randomizer = FlexRandomizer({
'pwrite': ([(0, 0), (1, 1)], [1, 1]),
'paddr': ([(0x1000, 0x1FFF), (0x8000, 0x8FFF)], [3, 1]), # Prefer low addresses
'pstrb': ([(0xF, 0xF), (0x3, 0x3), (0xC, 0xC)], [6, 1, 1]), # Various strobe patterns
'pprot': ([(0, 0)], [1]) # Always normal protection
})
# Create constrained transaction generator
constrained_transaction = APBTransaction(randomizer=address_randomizer)
# Generate constrained sequence
for i in range(20):
packet = constrained_transaction.next()
print(f"Addr: 0x{packet.paddr:04X}, Strb: 0b{packet.pstrb:04b}, Dir: {packet.direction}")
Register Access Patterns¶
def create_register_access_sequence(base_addr, num_regs):
"""Create sequence for register testing"""
packets = []
# Write pattern to all registers
for i in range(num_regs):
addr = base_addr + (i * 4)
data = 0xA0000000 + i
write_packet = APBPacket(
pwrite=1,
paddr=addr,
pwdata=data,
pstrb=0xF,
count=len(packets)
)
packets.append(write_packet)
# Read back all registers
for i in range(num_regs):
addr = base_addr + (i * 4)
read_packet = APBPacket(
pwrite=0,
paddr=addr,
count=len(packets)
)
packets.append(read_packet)
return packets
# Generate register test sequence
reg_packets = create_register_access_sequence(0x1000, 16)
Error Testing Patterns¶
def create_error_test_packets():
"""Create packets for error testing"""
packets = []
# Valid transaction
valid_packet = APBPacket(
pwrite=1,
paddr=0x100,
pwdata=0x12345678,
pstrb=0xF,
pslverr=0
)
packets.append(valid_packet)
# Transaction that should cause slave error
error_packet = APBPacket(
pwrite=1,
paddr=0xDEAD, # Invalid address
pwdata=0xBAADF00D,
pstrb=0xF,
pslverr=0 # Will be set by slave
)
packets.append(error_packet)
return packets
Strobe Pattern Testing¶
def create_strobe_test_packets(base_addr):
"""Create packets testing different strobe patterns"""
packets = []
test_data = 0x12345678
# Test individual byte strobes
for byte_pos in range(4):
strobe = 1 << byte_pos
packet = APBPacket(
pwrite=1,
paddr=base_addr,
pwdata=test_data,
pstrb=strobe,
count=len(packets)
)
packets.append(packet)
# Test combinations
strobe_patterns = [0x3, 0xC, 0x5, 0xA, 0x7, 0xE, 0xF]
for pattern in strobe_patterns:
packet = APBPacket(
pwrite=1,
paddr=base_addr + 4,
pwdata=test_data,
pstrb=pattern,
count=len(packets)
)
packets.append(packet)
return packets
Performance Testing¶
def create_performance_test_sequence(num_transactions):
"""Create high-performance test sequence"""
# Use transaction generator for variety
transaction = APBTransaction()
# Override randomizer for performance testing
perf_randomizer = FlexRandomizer({
'pwrite': ([(1, 1)], [1]), # All writes for performance
'paddr': ([(0x1000, 0x2000)], [1]), # Sequential addresses
'pstrb': ([(0xF, 0xF)], [1]), # Always full strobes
'pprot': ([(0, 0)], [1]) # Normal protection
})
transaction.randomizer = perf_randomizer
# Generate performance sequence
packets = []
for i in range(num_transactions):
packet = transaction.next()
packet.count = i
packets.append(packet)
return packets
Packet Validation¶
def validate_apb_packet(packet):
"""Validate APB packet for protocol compliance"""
errors = []
# Check required fields
if packet.pwrite not in [0, 1]:
errors.append("Invalid PWRITE value")
# Address alignment check (assuming 32-bit data)
if packet.paddr % 4 != 0:
errors.append("Address not word-aligned")
# Strobe validation for writes
if packet.direction == 'WRITE':
if packet.pstrb == 0:
errors.append("No strobes active for write")
# Check strobe-data consistency
if packet.pstrb > 0xF:
errors.append("Invalid strobe pattern")
# Protection field validation
if packet.pprot > 7:
errors.append("Invalid protection value")
return errors
# Validate packet
packet = APBPacket(pwrite=1, paddr=0x103, pwdata=0x123, pstrb=0xF)
validation_errors = validate_apb_packet(packet)
if validation_errors:
print(f"Validation errors: {validation_errors}")
Integration with Test Framework¶
Testbench Integration¶
import cocotb
from cocotb.triggers import RisingEdge
from CocoTBFramework.components.apb import APBMaster, APBPacket
@cocotb.test()
async def packet_driven_test(dut):
# Create master
master = APBMaster(dut, "Master", "apb_", dut.clk)
# Create test packets
packets = [
APBPacket(pwrite=1, paddr=0x100, pwdata=0x11111111),
APBPacket(pwrite=1, paddr=0x104, pwdata=0x22222222),
APBPacket(pwrite=0, paddr=0x100),
APBPacket(pwrite=0, paddr=0x104)
]
# Send packets
for packet in packets:
await master.send(packet)
print(f"Sent: {packet.formatted(compact=True)}")
# Wait for completion
while master.transfer_busy:
await RisingEdge(dut.clk)
Scoreboard Integration¶
class APBScoreboard:
def __init__(self):
self.expected_packets = []
self.actual_packets = []
def add_expected(self, packet):
self.expected_packets.append(packet)
def add_actual(self, packet):
self.actual_packets.append(packet)
self.check_transaction()
def check_transaction(self):
if len(self.actual_packets) <= len(self.expected_packets):
expected = self.expected_packets[len(self.actual_packets) - 1]
actual = self.actual_packets[-1]
if expected == actual:
print("✓ Transaction match")
else:
print(f"✗ Transaction mismatch:")
print(f" Expected: {expected.formatted(compact=True)}")
print(f" Actual: {actual.formatted(compact=True)}")
Best Practices¶
1. Use Appropriate Field Widths¶
# Match your DUT configuration
packet = APBPacket(
data_width=32, # Match DUT data width
addr_width=16, # Match DUT address width
strb_width=4 # data_width / 8
)
2. Validate Packets Before Use¶
# Always validate critical fields
assert packet.direction in ['READ', 'WRITE']
assert packet.paddr % 4 == 0 # Word alignment
if packet.direction == 'WRITE':
assert packet.pstrb > 0 # At least one strobe
3. Use Transactions for Random Testing¶
# Use transaction generators for stimulus
transaction = APBTransaction(randomizer=custom_randomizer)
for _ in range(1000):
packet = transaction.next()
# Send to DUT
4. Handle Timing Information¶
# Set timing for performance analysis
import cocotb
packet.start_time = cocotb.utils.get_sim_time('ns')
# ... send packet ...
packet.end_time = cocotb.utils.get_sim_time('ns')
duration = packet.end_time - packet.start_time
5. Use Compact Format for Logging¶
# Use compact format in loops
for packet in packet_sequence:
print(f"Processing: {packet.formatted(compact=True)}")
The APB packet classes provide a robust foundation for APB transaction handling, from simple directed tests to complex randomized stimulus generation, with full integration into the verification framework.