|
CocoTB Framework · Verification Infrastructure for RTL Testing GitHub · Documentation Index · MIT License |
AXISFieldConfigs¶
AXISFieldConfigs is the factory that builds field configurations for the AXI4-Stream components. A field configuration is the testbench-side description of your bus — how wide TDATA is, which sideband signals exist, how wide they are — and this class exists so that description gets written down exactly once. Build the config from the same parameters you gave the RTL, hand it to the master, the slave, the monitor and every packet, and a 12-bit TID in the SystemVerilog is a 12-bit TID in the scoreboard. No drift, no per-component guesswork.
Class Overview¶
The docstring spells out what lives on an AXIS T channel:
class AXISFieldConfigs:
"""
Factory class for creating AXIS field configurations.
AXI4-Stream protocol uses a single T channel containing:
- TDATA: Data payload
- TSTRB: Byte enables (strobe)
- TLAST: End of packet/frame indicator
- TID: Stream identifier
- TDEST: Destination routing
- TUSER: User-defined sideband data
- TVALID/TREADY: Handshake signals
"""
Factory Methods¶
Four ways to get a config, depending on how much you already know about the bus.
Core Configuration Method¶
create_t_field_config(data_width=32, id_width=8, dest_width=4, user_width=1)¶
The general-purpose factory — every other method on this page is a wrapper around this one. All optional sidebands are available here; set a width to 0 for any signal your design doesn't have.
Parameters:
- data_width (int) - Width of TDATA field (8, 16, 32, 64, 128, 256, 512, 1024)
- id_width (int) - Width of TID field (1-16 bits, 0 to disable)
- dest_width (int) - Width of TDEST field (1-16 bits, 0 to disable)
- user_width (int) - Width of TUSER field (1-32 bits, 0 to disable)
Returns: FieldConfig - Complete field configuration for T channel
Example:
# Create full-featured AXIS configuration
config = AXISFieldConfigs.create_t_field_config(
data_width=64,
id_width=16,
dest_width=8,
user_width=16
)
# Create configuration with minimal sideband signals
config = AXISFieldConfigs.create_t_field_config(
data_width=32,
id_width=4,
dest_width=0, # No destination routing
user_width=0 # No user data
)
Predefined Configurations¶
Three shortcuts for the cases that come up over and over.
create_default_axis_config(data_width=32)¶
The middle-of-the-road config: 8-bit ID, 4-bit DEST, 1-bit USER. If your stream has sidebands but nothing exotic about them, start here.
Parameters:
- data_width (int) - Width of data field
Returns: FieldConfig - Standard configuration (8-bit ID, 4-bit DEST, 1-bit USER)
Example:
# Create standard 32-bit AXIS configuration
config = AXISFieldConfigs.create_default_axis_config(data_width=32)
# Results in: data=32b, id=8b, dest=4b, user=1b, strb=4b
# Create standard 64-bit AXIS configuration
config = AXISFieldConfigs.create_default_axis_config(data_width=64)
# Results in: data=64b, id=8b, dest=4b, user=1b, strb=8b
create_simple_axis_config(data_width=32)¶
Data, strobe, last — nothing else. The right choice for a plain data pipe with no routing and no sideband baggage.
Parameters:
- data_width (int) - Width of data field
Returns: FieldConfig - Minimal configuration (no ID, DEST, or USER signals)
Example:
# Create simple streaming configuration
config = AXISFieldConfigs.create_simple_axis_config(data_width=32)
# Results in: data=32b, strb=4b, last=1b only
# Create simple wide configuration
config = AXISFieldConfigs.create_simple_axis_config(data_width=512)
# Results in: data=512b, strb=64b, last=1b only
create_axis_config_from_hw_params(data_width, id_width, dest_width, user_width)¶
Feed it the exact parameter values from your SystemVerilog module and get back a config that matches signal-for-signal — including zero-width parameters, which is how the RTL says "this signal doesn't exist" and how the config says it too.
Parameters:
- data_width (int) - AXIS_DATA_WIDTH hardware parameter
- id_width (int) - AXIS_ID_WIDTH hardware parameter
- dest_width (int) - AXIS_DEST_WIDTH hardware parameter
- user_width (int) - AXIS_USER_WIDTH hardware parameter
Returns: FieldConfig - Configuration matching hardware exactly
Example:
# Match hardware module parameters
config = AXISFieldConfigs.create_axis_config_from_hw_params(
data_width=128,
id_width=12,
dest_width=6,
user_width=8
)
# Zero-width parameter handling (hardware with TID_WIDTH=0)
config = AXISFieldConfigs.create_axis_config_from_hw_params(
data_width=32,
id_width=0, # No ID signal
dest_width=0, # No DEST signal
user_width=4 # 4-bit USER signal
)
Field Definitions¶
Every config the factory produces is built from these fields. The core three are always present; the sidebands appear only when their width is nonzero.
Core Fields (Always Present)¶
data Field¶
- Width: Configurable (8-1024 bits)
- Format: Hexadecimal
- Default: 0
- Description: Stream data payload — TDATA for the beat
strb Field¶
- Width: data_width / 8 bits
- Format: Binary
- Default: All bits set (all bytes enabled)
- Description: Byte enables — one bit per byte lane, marking which bytes of
dataare valid
last Field¶
- Width: 1 bit
- Format: Binary
- Default: 0 (not last)
- Description: TLAST — end of packet/frame indicator
- Encoding: {0: "Not Last", 1: "Last"}
Optional Fields (Conditionally Present)¶
id Field (if id_width > 0)¶
- Width: Configurable (1-16 bits)
- Format: Hexadecimal
- Default: 0
- Description: TID — stream identifier, for when the bus carries several streams
dest Field (if dest_width > 0)¶
- Width: Configurable (1-16 bits)
- Format: Hexadecimal
- Default: 0
- Description: TDEST — destination routing identifier
user Field (if user_width > 0)¶
- Width: Configurable (1-32 bits)
- Format: Binary
- Default: 0
- Description: TUSER — user-defined sideband data; the BFM carries it without interpreting it
Usage Examples¶
Basic Configuration Setup¶
# Create configuration for different data widths
configs = {}
# 32-bit standard configuration
configs['axis32'] = AXISFieldConfigs.create_default_axis_config(32)
# 64-bit high-performance configuration
configs['axis64'] = AXISFieldConfigs.create_t_field_config(
data_width=64,
id_width=16,
dest_width=8,
user_width=32
)
# 512-bit simple streaming configuration
configs['axis512'] = AXISFieldConfigs.create_simple_axis_config(512)
# Use with components
master = AXISMaster(dut, "Master", "m_axis_", clk, field_config=configs['axis32'])
Hardware Matching Configuration¶
This is the pattern I'd recommend as the default: read the parameters off the DUT once, build the config from them, and never think about widths again.
# Match SystemVerilog module parameters
# module axis_master #(
# parameter AXIS_DATA_WIDTH = 128,
# parameter AXIS_ID_WIDTH = 8,
# parameter AXIS_DEST_WIDTH = 4,
# parameter AXIS_USER_WIDTH = 16
# )
hw_config = AXISFieldConfigs.create_axis_config_from_hw_params(
data_width=128,
id_width=8,
dest_width=4,
user_width=16
)
# Create components with matching configuration
master = AXISMaster(dut, "HW_Master", "m_axis_", clk, field_config=hw_config)
slave = AXISSlave(dut, "HW_Slave", "s_axis_", clk, field_config=hw_config)
Dynamic Configuration Selection¶
def get_axis_config(stream_type, data_width):
"""Select appropriate AXIS configuration based on requirements."""
if stream_type == "simple":
return AXISFieldConfigs.create_simple_axis_config(data_width)
elif stream_type == "routing":
return AXISFieldConfigs.create_t_field_config(
data_width=data_width,
id_width=8,
dest_width=8,
user_width=0
)
elif stream_type == "full_featured":
return AXISFieldConfigs.create_t_field_config(
data_width=data_width,
id_width=16,
dest_width=16,
user_width=32
)
else:
return AXISFieldConfigs.create_default_axis_config(data_width)
# Use in test scenarios
test_config = get_axis_config("routing", 64)
master = AXISMaster(dut, "RoutingMaster", "m_axis_", clk, field_config=test_config)
Configuration Analysis and Validation¶
Handy when a test fails and your first question is "wait, what config did this thing actually get built with?"
def analyze_axis_config(config):
"""Analyze AXIS field configuration."""
analysis = {
'total_fields': len(config),
'data_width': config.get_field('data').bits,
'strb_width': config.get_field('strb').bits,
'has_id': config.has_field('id'),
'has_dest': config.has_field('dest'),
'has_user': config.has_field('user')
}
# Calculate total bus width
total_bits = sum(field.bits for field in config.fields)
analysis['total_signal_bits'] = total_bits
# Add optional field widths
if analysis['has_id']:
analysis['id_width'] = config.get_field('id').bits
if analysis['has_dest']:
analysis['dest_width'] = config.get_field('dest').bits
if analysis['has_user']:
analysis['user_width'] = config.get_field('user').bits
return analysis
# Analyze different configurations
configs_to_test = [
AXISFieldConfigs.create_simple_axis_config(32),
AXISFieldConfigs.create_default_axis_config(32),
AXISFieldConfigs.create_t_field_config(64, 16, 8, 32)
]
for i, config in enumerate(configs_to_test):
analysis = analyze_axis_config(config)
print(f"Config {i}: {analysis}")
Zero-Width Signal Handling¶
# Handle configurations with zero-width optional signals
def create_flexible_axis_config(data_width, **optional_widths):
"""Create AXIS config with flexible optional signal widths."""
return AXISFieldConfigs.create_t_field_config(
data_width=data_width,
id_width=optional_widths.get('id_width', 0),
dest_width=optional_widths.get('dest_width', 0),
user_width=optional_widths.get('user_width', 0)
)
# Various zero-width combinations
configs = {
'data_only': create_flexible_axis_config(32),
'with_id': create_flexible_axis_config(32, id_width=8),
'with_routing': create_flexible_axis_config(32, id_width=4, dest_width=4),
'full_custom': create_flexible_axis_config(32, id_width=12, dest_width=6, user_width=8)
}
# Verify field presence
for name, config in configs.items():
fields = config.field_names()
print(f"{name}: {fields}")
Integration with Components¶
Component Configuration¶
The point of the shared config: master and slave built from it cannot disagree about the bus.
# Configure master and slave with matching configurations
config = AXISFieldConfigs.create_default_axis_config(64)
master = AXISMaster(
dut=dut,
title="ConfiguredMaster",
prefix="m_axis_",
clock=clk,
field_config=config
)
slave = AXISSlave(
dut=dut,
title="ConfiguredSlave",
prefix="s_axis_",
clock=clk,
field_config=config
)
# Configuration ensures field compatibility
Packet Creation with Configuration¶
# Create packets using configuration
config = AXISFieldConfigs.create_t_field_config(32, 8, 4, 16)
packet = AXISPacket(field_config=config)
packet.data = 0x12345678
packet.strb = 0xF
packet.last = 1
packet.id = 5
packet.dest = 2
packet.user = 0xABCD
# All fields properly sized according to configuration
That's the whole class — a small factory doing a boring job, which is exactly what you want from it. The width-mismatch bugs it prevents are the kind that otherwise surface three hours into a debug session as mysteriously truncated sidebands. Build the config from the hardware parameters and those bugs simply don't happen.