|
CocoTB Framework · Verification Infrastructure for RTL Testing GitHub · Documentation Index · MIT License |
fifo_scoreboard.py¶
FIFO protocol scoreboard implementation for verifying FIFO transactions with memory model integration and field-configurable packet comparison. This module provides comprehensive verification for FIFO-based communication systems.
Overview¶
The FIFO scoreboard system provides: - Field-based Comparison: Uses FieldConfig for flexible packet structure verification - Memory Model Integration: Built-in memory adapter for data consistency checking - FIFO Packet Support: Native handling of FIFO packet classes with enhanced logging - Configurable Verification: Customizable field mapping and comparison logic
Classes¶
FIFOScoreboard¶
Core FIFO transaction verification with field-configurable comparison.
Parameters:
- name: Scoreboard name for identification
- field_config: Field configuration defining packet structure
- log: Logger instance for detailed reporting
Key Features: - Uses FieldConfig for flexible packet structure - Enhanced mismatch logging with field-by-field analysis - Integration with FIFOPacket class - Configurable comparison logic
Core Methods¶
Transaction Comparison¶
_compare_transactions(expected, actual)¶
Compare FIFO packets using built-in packet equality.
Parameters:
- expected: Expected FIFO transaction (FIFOPacket)
- actual: Actual FIFO transaction (FIFOPacket)
Returns:
- bool: True if packets match, False otherwise
Comparison Logic:
- Validates transaction types (must be FIFOPacket instances)
- Uses FIFOPacket's built-in __eq__ method
- Compares all fields defined in field_config
# Automatic comparison when both transactions available
scoreboard.add_expected(expected_fifo_packet)
scoreboard.add_actual(actual_fifo_packet) # Triggers comparison
_log_mismatch(expected, actual)¶
Enhanced mismatch logging with detailed field analysis.
Parameters:
- expected: Expected FIFO packet
- actual: Actual FIFO packet
Detailed Logging:
- Uses packet's formatted(compact=True) method for readable output
- Field-by-field comparison using field_config
- Hexadecimal display for mismatched field values
- Clear identification of specific field mismatches
# Example mismatch log output:
# FIFO Packet mismatch:
# Expected: data=0xDEADBEEF, ctrl=0x1, valid=1
# Actual: data=0xBEEFDEAD, ctrl=0x1, valid=1
# Field 'data' mismatch: expected=0xDEADBEEF, actual=0xBEEFDEAD
Memory Model Integration¶
MemoryAdapter¶
Adapter class for integrating FIFO packets with memory models for data consistency verification.
Parameters:
- memory_model: Memory model instance for data storage and retrieval
- field_map: Dictionary mapping memory operations to packet fields
- log: Logger instance for operation tracking
Default Field Mapping:
- 'addr': Address field for memory operations
- 'data': Data field for read/write operations
- 'ctrl': Control field for operation type
Memory Operations¶
write_to_memory(packet)¶
Write packet data to memory model based on field mapping.
Parameters:
- packet: FIFO packet containing write data
Behavior: - Extracts address from packet using field mapping - Writes data to memory at specified address - Logs write operation for debugging - Handles field mapping errors gracefully
# Write packet to memory
adapter = MemoryAdapter(memory_model, field_map={'addr': 'address', 'data': 'payload'})
adapter.write_to_memory(fifo_packet)
read_from_memory(packet)¶
Read data from memory model and compare with packet.
Parameters:
- packet: FIFO packet containing expected read data
Returns:
- bool: True if memory data matches packet data
Behavior: - Extracts address from packet - Reads data from memory at address - Compares memory data with packet data field - Returns match result for verification
# Verify read data matches memory
match = adapter.read_from_memory(read_packet)
if not match:
print("Memory data mismatch detected")
verify_packet_consistency(packet)¶
Comprehensive packet verification against memory state.
Parameters:
- packet: FIFO packet to verify
Returns:
- dict: Verification results with detailed status
Verification Checks: - Address field validity - Data consistency with memory contents - Control field interpretation - Field mapping completeness
# Comprehensive verification
results = adapter.verify_packet_consistency(packet)
print(f"Verification status: {results['status']}")
print(f"Details: {results['details']}")
Usage Examples¶
Basic FIFO Verification¶
from CocoTBFramework.scoreboards.fifo_scoreboard import FIFOScoreboard
from CocoTBFramework.components.fifo.fifo_packet import FIFOPacket
from CocoTBFramework.components.shared.field_config import FieldConfig
# Define field configuration
field_config = FieldConfig.from_dict({
'data': 32,
'valid': 1,
'ready': 1,
'ctrl': 4
})
# Create scoreboard
scoreboard = FIFOScoreboard("FIFO_Test", field_config, log=logger)
# Create test packets
expected = FIFOPacket(field_config)
expected.fields['data'] = 0xDEADBEEF
expected.fields['valid'] = 1
expected.fields['ready'] = 1
expected.fields['ctrl'] = 0x5
actual = FIFOPacket(field_config)
actual.fields['data'] = 0xDEADBEEF
actual.fields['valid'] = 1
actual.fields['ready'] = 1
actual.fields['ctrl'] = 0x5
# Verify transactions
scoreboard.add_expected(expected)
scoreboard.add_actual(actual)
# Check results
error_count = scoreboard.report()
pass_rate = scoreboard.result()
print(f"FIFO Verification: {'PASS' if error_count == 0 else 'FAIL'} ({pass_rate:.2%})")
Memory-Backed FIFO Verification¶
from CocoTBFramework.scoreboards.fifo_scoreboard import MemoryAdapter
from CocoTBFramework.components.shared.memory_model import MemoryModel
# Create memory model and adapter
memory = MemoryModel(size=1024*1024, log=logger)
field_map = {
'addr': 'address',
'data': 'payload',
'ctrl': 'command'
}
adapter = MemoryAdapter(memory, field_map, log=logger)
# Create memory-integrated scoreboard
scoreboard = FIFOScoreboard("MemoryFIFO", field_config, log=logger)
# Test memory consistency
write_packet = FIFOPacket(field_config)
write_packet.fields['address'] = 0x1000
write_packet.fields['payload'] = 0x12345678
write_packet.fields['command'] = 0x1 # Write command
# Write to memory
adapter.write_to_memory(write_packet)
# Create expected read packet
read_packet = FIFOPacket(field_config)
read_packet.fields['address'] = 0x1000
read_packet.fields['payload'] = 0x12345678
read_packet.fields['command'] = 0x0 # Read command
# Verify read consistency
match = adapter.read_from_memory(read_packet)
print(f"Memory consistency: {'PASS' if match else 'FAIL'}")
# Use with scoreboard for automated verification
scoreboard.add_expected(read_packet)
# ... actual packet from DUT ...
scoreboard.add_actual(actual_read_packet)
Advanced FIFO System Verification¶
# Multi-channel FIFO verification
async def test_multi_channel_fifo():
# Define complex field configuration
field_config = FieldConfig.from_dict({
'data': 64,
'channel': 4,
'valid': 1,
'ready': 1,
'last': 1,
'user': 8
})
# Create channel-specific scoreboards
scoreboards = {}
for channel in range(16):
scoreboards[channel] = FIFOScoreboard(
f"Channel_{channel}",
field_config,
log=logger
)
# Create test traffic generator
class ChannelTrafficGenerator:
def __init__(self, field_config):
self.field_config = field_config
self.packet_id = 0
def generate_packet(self, channel, data_pattern):
packet = FIFOPacket(self.field_config)
packet.fields['data'] = data_pattern
packet.fields['channel'] = channel
packet.fields['valid'] = 1
packet.fields['ready'] = 1
packet.fields['last'] = 0
packet.fields['user'] = self.packet_id & 0xFF
self.packet_id += 1
return packet
def generate_burst(self, channel, length, base_data):
packets = []
for i in range(length):
data = base_data + i
packet = self.generate_packet(channel, data)
if i == length - 1:
packet.fields['last'] = 1
packets.append(packet)
return packets
generator = ChannelTrafficGenerator(field_config)
# Generate test patterns for each channel
for channel in range(4): # Test first 4 channels
burst = generator.generate_burst(channel, 16, 0x1000 + channel * 0x100)
for packet in burst:
scoreboards[channel].add_expected(packet)
# Simulate DUT operation and verify
# ... DUT simulation code ...
# Generate verification report
total_errors = 0
for channel, scoreboard in scoreboards.items():
errors = scoreboard.report()
total_errors += errors
if errors > 0:
print(f"Channel {channel}: {errors} errors")
else:
print(f"Channel {channel}: PASS")
print(f"Overall: {'PASS' if total_errors == 0 else 'FAIL'} ({total_errors} total errors)")
Performance Analysis Integration¶
# FIFO performance verification
class PerformanceFIFOScoreboard(FIFOScoreboard):
def __init__(self, name, field_config, log=None):
super().__init__(name, field_config, log)
self.throughput_data = []
self.latency_data = []
self.start_time = None
def add_actual(self, transaction):
# Record timing data
if hasattr(transaction, 'timestamp'):
if self.start_time is None:
self.start_time = transaction.timestamp
# Calculate throughput
elapsed = transaction.timestamp - self.start_time
if elapsed > 0:
throughput = self.transaction_count / elapsed
self.throughput_data.append(throughput)
# Call parent method
super().add_actual(transaction)
def _compare_transactions(self, expected, actual):
# Calculate latency if both have timestamps
if hasattr(expected, 'timestamp') and hasattr(actual, 'timestamp'):
latency = actual.timestamp - expected.timestamp
self.latency_data.append(latency)
return super()._compare_transactions(expected, actual)
def get_performance_stats(self):
stats = {
'avg_throughput': sum(self.throughput_data) / len(self.throughput_data) if self.throughput_data else 0,
'max_throughput': max(self.throughput_data) if self.throughput_data else 0,
'avg_latency': sum(self.latency_data) / len(self.latency_data) if self.latency_data else 0,
'max_latency': max(self.latency_data) if self.latency_data else 0,
'total_transactions': self.transaction_count
}
return stats
# Usage
perf_scoreboard = PerformanceFIFOScoreboard("PerfTest", field_config, log=logger)
# ... run test ...
stats = perf_scoreboard.get_performance_stats()
print(f"Average Throughput: {stats['avg_throughput']:.2f} transactions/ns")
print(f"Average Latency: {stats['avg_latency']:.2f} ns")
Custom Field Verification¶
# Custom field validation scoreboard
class CustomFIFOScoreboard(FIFOScoreboard):
def __init__(self, name, field_config, custom_validators=None, log=None):
super().__init__(name, field_config, log)
self.custom_validators = custom_validators or {}
self.validation_errors = {}
def _compare_transactions(self, expected, actual):
# Standard comparison
basic_match = super()._compare_transactions(expected, actual)
# Custom field validation
validation_passed = True
for field_name, validator in self.custom_validators.items():
if field_name in expected.fields and field_name in actual.fields:
try:
if not validator(expected.fields[field_name], actual.fields[field_name]):
validation_passed = False
self.validation_errors[field_name] = self.validation_errors.get(field_name, 0) + 1
if self.log:
self.log.error(f"Custom validation failed for field '{field_name}'")
except Exception as e:
validation_passed = False
if self.log:
self.log.error(f"Validation error for field '{field_name}': {e}")
return basic_match and validation_passed
# Custom validators
def validate_data_range(expected, actual):
"""Validate data is within acceptable range of expected value"""
tolerance = 0x10 # Allow small variation
return abs(expected - actual) <= tolerance
def validate_checksum(expected, actual):
"""Validate data checksum"""
def calc_checksum(data):
return (data ^ (data >> 16)) & 0xFFFF
return calc_checksum(expected) == calc_checksum(actual)
# Usage
validators = {
'data': validate_data_range,
'checksum': validate_checksum
}
custom_scoreboard = CustomFIFOScoreboard(
"CustomValidation",
field_config,
custom_validators=validators,
log=logger
)
Best Practices¶
Field Configuration¶
- Define clear field mappings that match DUT interface
- Use consistent field naming across components
- Document field semantics and valid ranges
Memory Model Integration¶
- Configure appropriate memory size for test requirements
- Use realistic addressing patterns
- Clear memory state between test phases when needed
Performance Optimization¶
- Use efficient field comparison for high-throughput tests
- Monitor memory usage with large packet volumes
- Consider batch verification for improved performance
Error Analysis¶
- Enable detailed logging for field-level mismatch analysis
- Use custom validators for domain-specific checks
- Preserve packet history for temporal analysis
Integration Points¶
Monitor Integration¶
# Connect FIFO monitor to scoreboard
def on_fifo_packet(packet):
scoreboard.add_actual(packet)
fifo_monitor.add_callback(on_fifo_packet)
Test Sequence Integration¶
# Generate expected packets from sequence
sequence = FIFOSequence("test_pattern", field_config)
for packet in sequence.generate():
scoreboard.add_expected(packet)
Coverage Integration¶
# Field coverage analysis
def analyze_field_coverage(scoreboard):
field_values = {}
for packet in scoreboard.get_all_packets():
for field, value in packet.fields.items():
if field not in field_values:
field_values[field] = set()
field_values[field].add(value)
for field, values in field_values.items():
coverage = len(values) / (2 ** field_config[field]) * 100
print(f"Field '{field}' coverage: {coverage:.1f}%")
The FIFO scoreboard provides comprehensive verification capabilities for FIFO-based systems with flexible field configuration, memory model integration, and extensive customization options for domain-specific verification requirements.