|
CocoTB Framework · Verification Infrastructure for RTL Testing GitHub · Documentation Index · MIT License |
AXISMaster¶
AXISMaster is the stream source: it drives the AXI4-Stream T channel as a master. All of the actual bus driving is done by the GAXI transmit pipeline it inherits — this class is the stream-shaped layer on top. It gives you send_stream_data and friends, handles TLAST so frames end where they should, and counts packets and frames as they go out.
Class Overview¶
class AXISMaster(GAXIMaster):
"""
AXIS Master component for driving AXI4-Stream protocol.
Inherits the transmit pipeline from GAXIMaster:
- Signal resolution and data driving setup
- Structured transmit pipeline with handshake/timeout handling
- Unified field configuration handling
- Statistics and logging patterns
AXIS-specific features added by this subclass:
- Stream/frame conveniences (send_stream_data, send_frame,
send_single_beat)
- Packet/frame boundary handling with TLAST
- Frame-level statistics (packets_sent / frames_sent)
"""
Delegation to the GAXI Pipeline¶
All bus driving is performed by GAXIMaster's structured transmit pipeline. send_packet queues one packet on that pipeline and awaits completion; send_stream_data (and therefore send_frame) queues every beat up front so they stream back-to-back — the GAXI pipeline keeps TVALID asserted between queued beats for zero-bubble operation when the slave holds TREADY high — and then waits for the pipeline to drain.
AXISMaster keeps no send queue, busy flag, or drive loop of its own. is_busy() and get_queue_depth() report the state of the inherited GAXI transmit queue (self.transmit_queue / self.transfer_busy).
Timeout behavior — worth knowing before you write your first test: a TREADY handshake timeout does not come back as False. The GAXI pipeline detects it and raises cocotb's TestFailure after timeout_cycles. The send_* methods return True on success precisely because there is no False path — failure is an exception, so write your test accordingly.
Constructor¶
__init__(dut, title, prefix, clock, **kwargs)¶
Construct the master and resolve the bus signals sitting under prefix.
Parameters:
- dut - Device under test instance
- title (str) - Component title/name for identification and logging
- prefix (str) - Signal prefix (e.g., "m_axis_", "fub_axis_")
- clock - Clock signal reference
Optional Parameters:
- field_config (AXISFieldConfigs) - Field configuration (creates default if None)
- timeout_cycles (int) - Maximum cycles to wait for ready (default: 1000)
- mode (str) - Protocol mode ('skid', 'blocking', etc.)
- bus_name (str) - Bus/channel name for identification
- pkt_prefix (str) - Packet field prefix
- multi_sig (bool) - Whether using multi-signal mode
- randomizer - Optional randomizer for timing variations
- memory_model - Optional memory model integration
- log - Logger instance for debug output
- super_debug (bool) - Enable detailed debugging
- pipeline_debug (bool) - Enable pipeline debugging
- signal_map (dict) - Optional manual signal mapping
Example:
master = AXISMaster(
dut=dut,
title="StreamSource",
prefix="m_axis_",
clock=clk,
timeout_cycles=2000,
super_debug=True
)
Core Methods¶
Stream Data Transmission¶
send_stream_data(data_list, **kwargs) (async)¶
The workhorse. Hand it a list of data values and it sends them down the stream as one frame, with TLAST handled for you.
Parameters:
- data_list (list) - List of data values to send
- id (int) - Stream ID for all transfers (default: 0)
- dest (int) - Destination for all transfers (default: 0)
- user (int) - User signal for all transfers (default: 0)
- auto_last (bool) - Automatically set TLAST on final transfer (default: True)
- strb_list (list) - Optional list of strobe values
Returns: bool - True if successful (a TREADY timeout raises from the GAXI pipeline)
Example:
# Send a stream of data with automatic TLAST
data = [0x11111111, 0x22222222, 0x33333333, 0x44444444]
success = await master.send_stream_data(
data_list=data,
id=5,
dest=2,
user=0xABCD
)
send_packet(packet) (async)¶
Send one packet exactly as you built it — every field on the wire comes from the packet object, so this is the method to use when you need control the conveniences don't give you.
Parameters:
- packet (AXISPacket) - Configured packet to send
Returns: bool - True if successful (a TREADY timeout raises from the GAXI pipeline)
Example:
# Create and send a custom packet
packet = AXISPacket(field_config=master.field_config)
packet.data = 0x12345678
packet.last = 1
packet.id = 3
packet.dest = 1
packet.user = 0x1000
success = await master.send_packet(packet)
send_frame(frame_data, **kwargs) (async)¶
Send a complete frame: multiple beats, TLAST on the final one. A thin wrapper over send_stream_data with frame-flavored argument names.
Parameters:
- frame_data (list) - List of data values for the frame
- frame_id (int) - Frame ID (default: 0)
- dest (int) - Destination (default: 0)
- user (int) - User signal (default: 0)
Returns: bool - True if successful
Example:
# Send a complete frame
frame = [0xDEADBEEF, 0xCAFEBABE, 0x12345678]
success = await master.send_frame(
frame_data=frame,
frame_id=7,
dest=3
)
send_single_beat(data, **kwargs) (async)¶
Send one beat. TLAST defaults to asserted, so out of the box this is a complete one-beat frame — set last=0 explicitly if more beats are coming.
Parameters:
- data - Data value to send
- last (int) - TLAST value (default: 1)
- id (int) - Stream ID (default: 0)
- dest (int) - Destination (default: 0)
- user (int) - User signal (default: 0)
- strb (int) - Strobe value (auto-generated if None)
Returns: bool - True if successful
Example:
# Send single beat with custom fields
success = await master.send_single_beat(
data=0xABCDEF01,
last=0, # Not end of packet
id=2,
dest=1,
user=0x5555,
strb=0xF # All bytes valid
)
Status and Control Methods¶
is_busy()¶
True while anything is queued or mid-transfer on the inherited transmit pipeline.
Returns: bool - True if transactions are queued or active
get_queue_depth()¶
How many packets are still waiting in the inherited GAXI transmit queue.
Returns: int - Number of packets waiting in the inherited GAXI transmit queue
get_stats()¶
The counters, in one dict:
Returns: dict - Statistics dictionary containing:
- packets_sent - Total packets transmitted
- frames_sent - Total frames transmitted
- total_data_bytes - Total bytes transferred
- timeouts - Number of timeout events
- errors - Number of failed transactions
- queue_depth - Current queue depth
- is_busy - Current busy status
Example:
stats = master.get_stats()
print(f"Sent {stats['packets_sent']} packets, {stats['frames_sent']} frames")
print(f"Queue depth: {stats['queue_depth']}, Busy: {stats['is_busy']}")
Advanced Features¶
Flow Control and Timing¶
Things the master handles without being asked:
- TREADY backpressure: the pipeline holds TVALID and waits — it never drops a beat because the slave wasn't ready.
- Timeout protection: if TREADY stays low for
timeout_cycles, the pipeline raises instead of hanging your test forever. - Zero-bubble streaming: beats queued back-to-back go out back-to-back when TREADY stays high.
- Timing randomization: pass a randomizer at construction if you want a less perfectly-behaved source.
Memory Model Integration¶
from CocoTBFramework.components.shared.memory_model import MemoryModel
# Attach a memory model at construction time
memory = MemoryModel(num_lines=256, bytes_per_line=4)
master = AXISMaster(dut, "Source", "m_axis_", clk, memory_model=memory)
# Memory is automatically updated with sent data
await master.send_stream_data([0x1000, 0x2000, 0x3000])
Statistics and Monitoring¶
The counters are the honest kind — they count what actually completed on the bus:
- Packet and frame counters
- Byte transfer tracking
- Timeout and error monitoring
- Queue depth monitoring
- Performance metrics
Debug and Logging¶
# Enable detailed debugging
master = AXISMaster(
dut=dut,
title="DebugMaster",
prefix="m_axis_",
clock=clk,
super_debug=True,
pipeline_debug=True
)
# All transactions are logged with detailed information
Integration Examples¶
Basic Stream Generation¶
async def test_stream_generation():
# Create master
master = AXISMaster(dut, "Generator", "m_axis_", clk)
# Generate test data
test_data = [i * 0x11111111 for i in range(1, 17)]
# Send as stream
success = await master.send_stream_data(
data_list=test_data,
id=1,
dest=0
)
assert success, "Stream transmission failed"
# Verify statistics
stats = master.get_stats()
assert stats['packets_sent'] == 16
assert stats['frames_sent'] == 1
Multi-Stream Scenario¶
async def test_multi_stream():
master = AXISMaster(dut, "MultiStream", "m_axis_", clk)
# Send multiple concurrent streams
for stream_id in range(4):
stream_data = [0x1000 + stream_id + i for i in range(8)]
await master.send_stream_data(
data_list=stream_data,
id=stream_id,
dest=stream_id % 2
)
# Wait for completion
while master.is_busy():
await RisingEdge(clk)
Custom Packet Construction¶
async def test_custom_packets():
master = AXISMaster(dut, "CustomSender", "m_axis_", clk)
# Create packet with specific strobe pattern
packet = AXISPacket(field_config=master.field_config)
packet.data = 0x12345678
packet.strb = 0xC # Only upper 2 bytes valid
packet.last = 1
packet.id = 10
packet.user = 0xABCD
success = await master.send_packet(packet)
assert success
Error Handling¶
Failures surface as exceptions rather than silent drops — the TREADY timeout described above is the one you'll meet first. Beyond that, the component logs what it catches, tracks failed transactions in the statistics, and recovers so the test can continue or fail cleanly.
Common error scenarios: - TREADY timeout — the slave never accepted a beat (usually a dead DUT, a ready-generation bug, or a slave that was never configured) - Invalid packet configuration — a field value that doesn't fit its configured width - Memory model write failures - Clock domain issues — driving from the wrong clock
Performance Considerations¶
- Queueing: the transmit queue is a deque inherited from GAXI — cheap to append, cheap to drain.
- Back-to-back transfers: queued beats cost no extra cycles between them when the slave keeps TREADY high.
- Memory efficiency: data structures sized for high-throughput streams.
- Statistics overhead: counters increment on completion; you won't notice them in your simulation time.
If you remember one thing from this page: send_stream_data is the workhorse. The other send_* methods are conveniences that all end up in the same queue, driven by the same pipeline — pick whichever makes your test read best.