CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cocotb

Cocotb is a coroutine-based cosimulation library for writing VHDL and Verilog testbenches in Python.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

binary-types.mddocs/

Binary and Type System

Cocotb's binary and type system provides HDL-compatible data types for representing and manipulating binary values, logic states, arrays, and ranges. The system supports multiple binary representations, 4-value logic, and HDL-style indexing and slicing operations.

Capabilities

Binary Value Representation

Core class for representing binary values with multiple formats and automatic conversion capabilities.

class BinaryValue(value=None, n_bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED):
    """
    Binary value representation supporting multiple formats and bit manipulations.

    Parameters:
    - value: Initial value (int, str, bytes, or BinaryValue)
    - n_bits: Number of bits (auto-detected if None)
    - bigEndian: True for big-endian bit ordering
    - binaryRepresentation: Binary format (UNSIGNED, SIGNED_MAGNITUDE, TWOS_COMPLEMENT)

    Properties:
    - integer: Value as unsigned integer
    - signed_integer: Value as signed integer  
    - binstr: Binary string representation
    - buff: Raw buffer data
    - n_bits: Number of bits
    - is_resolvable: True if value contains no X/Z states

    Methods:
    - assign(value): Assign new value
    - hex(): Hexadecimal string representation
    """
    
    @property
    def integer(self) -> int:
        """Get value as unsigned integer."""
    
    @property  
    def signed_integer(self) -> int:
        """Get value as signed integer."""
    
    @property
    def binstr(self) -> str:
        """Get binary string representation."""
    
    @property
    def buff(self) -> bytes:
        """Get raw buffer data."""
    
    @property
    def n_bits(self) -> int:
        """Get number of bits."""
    
    @property
    def is_resolvable(self) -> bool:
        """Check if value contains no X/Z states."""
    
    def assign(self, value):
        """
        Assign new value to BinaryValue.
        
        Parameters:
        - value: New value to assign
        """
    
    def hex(self) -> str:
        """
        Get hexadecimal string representation.
        
        Returns:
        Hexadecimal string (e.g., "0xABCD")
        """

class BinaryRepresentation:
    """Binary representation format constants."""
    UNSIGNED = "UNSIGNED"
    SIGNED_MAGNITUDE = "SIGNED_MAGNITUDE"  
    TWOS_COMPLEMENT = "TWOS_COMPLEMENT"

Usage Examples:

@cocotb.test()
async def binary_value_test(dut):
    """Test demonstrating BinaryValue operations."""
    
    # Create binary values with different representations
    unsigned_val = BinaryValue(42, n_bits=8, binaryRepresentation=BinaryRepresentation.UNSIGNED)
    signed_val = BinaryValue(-42, n_bits=8, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT)
    
    # Access different representations
    cocotb.log.info(f"Unsigned 42: binary={unsigned_val.binstr}, hex={unsigned_val.hex()}")
    cocotb.log.info(f"Signed -42: binary={signed_val.binstr}, hex={signed_val.hex()}")
    
    # Convert from different sources
    from_string = BinaryValue("10101010", n_bits=8)
    from_int = BinaryValue(170, n_bits=8) 
    from_hex = BinaryValue(0xAA, n_bits=8)
    
    # All should be equivalent
    assert from_string.integer == from_int.integer == from_hex.integer
    
    # Assign to DUT signals
    dut.data_bus.value = unsigned_val
    await Timer(10, units="ns")
    
    # Read back and verify
    readback = BinaryValue(dut.data_bus.value, n_bits=8)
    assert readback.integer == 42

@cocotb.test()
async def binary_formats_test(dut):
    """Test different binary representation formats."""
    
    # Test unsigned format
    unsigned = BinaryValue(200, n_bits=8, binaryRepresentation=BinaryRepresentation.UNSIGNED)
    cocotb.log.info(f"Unsigned 200: {unsigned.integer} (0x{unsigned.integer:02X})")
    
    # Test two's complement 
    twos_comp = BinaryValue(-56, n_bits=8, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT)
    cocotb.log.info(f"2's complement -56: signed={twos_comp.signed_integer}, unsigned={twos_comp.integer}")
    
    # Test sign-magnitude (rarely used)
    sign_mag = BinaryValue(-56, n_bits=8, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE)
    cocotb.log.info(f"Sign-magnitude -56: {sign_mag.binstr}")
    
    # Test big vs little endian
    big_endian = BinaryValue(0x1234, n_bits=16, bigEndian=True)
    little_endian = BinaryValue(0x1234, n_bits=16, bigEndian=False)
    
    cocotb.log.info(f"Big endian 0x1234: {big_endian.binstr}")
    cocotb.log.info(f"Little endian 0x1234: {little_endian.binstr}")

Binary Utility Functions

Utility functions for binary string manipulation and X/Z value resolution.

def resolve(string):
    """
    Resolve X/Z values in binary string.
    
    Converts X and Z characters to 0 for numeric operations.
    
    Parameters:
    - string: Binary string potentially containing X/Z
    
    Returns:
    Binary string with X/Z resolved to 0
    """

Usage Examples:

@cocotb.test()
async def binary_utilities_test(dut):
    """Test demonstrating binary utility functions."""
    
    # Handle X/Z values in signals
    dut.test_signal.value = "10X1Z010"  # String with X/Z
    await Timer(1, units="ns")
    
    # Read back signal value
    signal_value = str(dut.test_signal.value)
    cocotb.log.info(f"Signal with X/Z: {signal_value}")
    
    # Resolve X/Z for numeric operations
    resolved = resolve(signal_value)
    cocotb.log.info(f"Resolved value: {resolved}")
    
    # Convert to numeric value
    numeric_val = BinaryValue(resolved)
    cocotb.log.info(f"Numeric value: {numeric_val.integer}")
    
    # Check if value is fully resolvable
    test_values = ["11010101", "110X0101", "110Z0101", "XXXXXXXX"]
    
    for test_val in test_values:
        bv = BinaryValue(test_val)
        cocotb.log.info(f"Value {test_val}: resolvable={bv.is_resolvable}")

4-Value Logic System

HDL-compatible logic types supporting 0, 1, X (unknown), and Z (high-impedance) states.

class Logic:
    """
    4-value logic type supporting 0, 1, X, Z states.
    
    Constants:
    - Logic.0: Logic 0 state
    - Logic.1: Logic 1 state  
    - Logic.X: Unknown/uninitialized state
    - Logic.Z: High-impedance state
    
    Operations:
    - & (and): Bitwise AND operation
    - | (or): Bitwise OR operation
    - ^ (xor): Bitwise XOR operation
    - ~ (not): Bitwise NOT operation
    """

# Type alias for backward compatibility  
Bit = Logic

class LogicArray:
    """
    Array of Logic values with HDL semantics.
    
    Properties:
    - binstr: Binary string representation
    - integer: Integer value (X/Z resolved to 0)
    - signed_integer: Signed integer value
    
    Methods:
    - to_BinaryValue(): Convert to BinaryValue object
    """
    
    @property
    def binstr(self) -> str:
        """Get binary string representation."""
    
    @property
    def integer(self) -> int:
        """Get integer value (X/Z resolved to 0)."""
    
    @property
    def signed_integer(self) -> int:
        """Get signed integer value."""
    
    def to_BinaryValue(self):
        """
        Convert to BinaryValue object.
        
        Returns:
        BinaryValue representation of the LogicArray
        """

Usage Examples:

@cocotb.test()
async def logic_types_test(dut):
    """Test demonstrating 4-value logic operations."""
    
    # Create individual logic values
    from cocotb.types import Logic
    
    zero = Logic.0
    one = Logic.1
    unknown = Logic.X
    high_z = Logic.Z
    
    # Logic operations
    and_result = one & zero    # Should be Logic.0
    or_result = one | zero     # Should be Logic.1
    xor_result = one ^ one     # Should be Logic.0
    not_result = ~zero         # Should be Logic.1
    
    cocotb.log.info(f"Logic operations:")
    cocotb.log.info(f"  1 & 0 = {and_result}")
    cocotb.log.info(f"  1 | 0 = {or_result}")
    cocotb.log.info(f"  1 ^ 1 = {xor_result}")
    cocotb.log.info(f"  ~0 = {not_result}")
    
    # Unknown value propagation
    unknown_and = one & unknown  # Should be Logic.X
    unknown_or = zero | unknown  # Should be Logic.X
    
    cocotb.log.info(f"Unknown propagation:")
    cocotb.log.info(f"  1 & X = {unknown_and}")
    cocotb.log.info(f"  0 | X = {unknown_or}")

@cocotb.test()
async def logic_array_test(dut):
    """Test demonstrating LogicArray operations."""
    
    from cocotb.types import LogicArray, Logic
    
    # Create logic arrays
    array1 = LogicArray([Logic.1, Logic.0, Logic.1, Logic.0])  # 0b1010
    array2 = LogicArray([Logic.0, Logic.1, Logic.X, Logic.Z])  # with X and Z
    
    # Access properties
    cocotb.log.info(f"Array1 binary: {array1.binstr}")
    cocotb.log.info(f"Array1 integer: {array1.integer}")
    
    cocotb.log.info(f"Array2 binary: {array2.binstr}")
    cocotb.log.info(f"Array2 integer: {array2.integer}")  # X/Z resolved to 0
    
    # Convert to BinaryValue
    bv1 = array1.to_BinaryValue()
    bv2 = array2.to_BinaryValue()
    
    cocotb.log.info(f"BinaryValue1: 0x{bv1.integer:X}")
    cocotb.log.info(f"BinaryValue2 resolvable: {bv2.is_resolvable}")
    
    # Use with DUT signals
    dut.logic_signal.value = array1
    await Timer(10, units="ns")

Container Types

HDL-compatible container types for arrays and ranges with proper indexing semantics.

class Array[T](obj, range):
    """
    Fixed-size arbitrarily-indexed array.
    
    Parameters:
    - obj: Object to wrap as array
    - range: Range object defining array bounds
    
    Properties:
    - range: Range object for array bounds
    
    Supports:
    - Indexing: array[index]
    - Slicing: array[start:end]  
    - Iteration: for item in array
    """
    
    @property
    def range(self):
        """Get range object defining array bounds."""

class Range(left, direction, right):
    """
    HDL-style inclusive range specification.
    
    Parameters:
    - left: Left bound of range
    - direction: "to" or "downto"
    - right: Right bound of range
    
    Properties:
    - left: Left bound
    - right: Right bound
    - direction: Range direction
    
    Methods:
    - to_range(): Convert to Python range object
    - from_range(range): Create Range from Python range
    """
    
    @property
    def left(self) -> int:
        """Get left bound of range."""
    
    @property
    def right(self) -> int:
        """Get right bound of range."""
    
    @property
    def direction(self) -> str:
        """Get range direction ('to' or 'downto')."""
    
    def to_range(self):
        """
        Convert to Python range object.
        
        Returns:
        Python range object with equivalent bounds
        """
    
    @classmethod
    def from_range(cls, range):
        """
        Create Range from Python range object.
        
        Parameters:
        - range: Python range object
        
        Returns:
        Range object with equivalent bounds
        """

Usage Examples:

@cocotb.test()
async def container_types_test(dut):
    """Test demonstrating container type operations."""
    
    from cocotb.types import Array, Range
    
    # Create HDL-style ranges
    range1 = Range(31, "downto", 0)  # 32-bit word, MSB first
    range2 = Range(0, "to", 7)       # 8-bit byte, LSB first
    
    cocotb.log.info(f"Range1: {range1.left} {range1.direction} {range1.right}")
    cocotb.log.info(f"Range2: {range2.left} {range2.direction} {range2.right}")
    
    # Convert to Python ranges for iteration
    py_range1 = range1.to_range()
    py_range2 = range2.to_range()
    
    cocotb.log.info(f"Python range1: {list(py_range1)}")
    cocotb.log.info(f"Python range2: {list(py_range2)}")
    
    # Create arrays with specific ranges
    if hasattr(dut, 'register_file'):
        reg_array = Array(dut.register_file, Range(31, "downto", 0))
        
        # Access array elements using HDL indexing
        first_reg = reg_array[31]  # MSB
        last_reg = reg_array[0]    # LSB
        
        # Set values
        first_reg.value = 0xDEAD
        last_reg.value = 0xBEEF
        
        await Timer(10, units="ns")
        
        cocotb.log.info(f"Register[31]: 0x{first_reg.value:X}")
        cocotb.log.info(f"Register[0]: 0x{last_reg.value:X}")

@cocotb.test()
async def array_slicing_test(dut):
    """Test demonstrating array slicing operations."""
    
    from cocotb.types import Array, Range
    
    # Create array representing a memory
    if hasattr(dut, 'memory_block'):
        memory = Array(dut.memory_block, Range(1023, "downto", 0))
        
        # Write test pattern to slice
        for i in range(16):
            memory[i].value = i * 0x10
        
        await Timer(50, units="ns")
        
        # Read back slice
        for i in range(16):
            value = memory[i].value
            expected = i * 0x10
            assert value == expected, f"Memory[{i}]: expected {expected}, got {value}"
        
        cocotb.log.info("Array slicing test passed")

Type Operations

Operations for combining and manipulating HDL-compatible types.

def concat(a, b, *args):
    """
    Concatenate arrays or values.
    
    Parameters:
    - a: First array/value
    - b: Second array/value  
    - *args: Additional arrays/values to concatenate
    
    Returns:
    Concatenated result
    """

Usage Examples:

@cocotb.test()
async def type_operations_test(dut):
    """Test demonstrating type operations."""
    
    from cocotb.types import LogicArray, Logic, concat
    
    # Create arrays to concatenate
    high_nibble = LogicArray([Logic.1, Logic.0, Logic.1, Logic.1])  # 0xB
    low_nibble = LogicArray([Logic.0, Logic.1, Logic.0, Logic.1])   # 0x5
    
    # Concatenate to form byte
    full_byte = concat(high_nibble, low_nibble)
    
    cocotb.log.info(f"High nibble: {high_nibble.binstr}")
    cocotb.log.info(f"Low nibble: {low_nibble.binstr}")
    cocotb.log.info(f"Concatenated: {full_byte.binstr}")
    cocotb.log.info(f"Result: 0x{full_byte.integer:02X}")
    
    # Multiple concatenation
    byte1 = LogicArray([Logic.1, Logic.1, Logic.0, Logic.0])  # 0xC
    byte2 = LogicArray([Logic.0, Logic.0, Logic.1, Logic.1])  # 0x3
    byte3 = LogicArray([Logic.1, Logic.0, Logic.1, Logic.0])  # 0xA
    
    word = concat(byte1, byte2, byte3, low_nibble)  # 16-bit word
    
    cocotb.log.info(f"Multi-concat result: {word.binstr}")
    cocotb.log.info(f"Word value: 0x{word.integer:04X}")
    
    # Use with DUT
    dut.concat_result.value = word
    await Timer(10, units="ns")

Advanced Type Usage

Custom Type Wrappers

class SignedByte:
    """Custom wrapper for signed 8-bit values."""
    
    def __init__(self, value=0):
        self._value = BinaryValue(value, n_bits=8, 
                                 binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT)
    
    @property
    def value(self):
        return self._value.signed_integer
    
    @value.setter
    def value(self, val):
        self._value.assign(val)
    
    @property
    def binary_value(self):
        return self._value
    
    def __str__(self):
        return f"SignedByte({self.value})"

@cocotb.test()
async def custom_types_test(dut):
    """Test demonstrating custom type wrappers."""
    
    # Create signed byte values
    sb1 = SignedByte(100)
    sb2 = SignedByte(-50)
    
    cocotb.log.info(f"Signed byte 1: {sb1}")
    cocotb.log.info(f"Signed byte 2: {sb2}")
    
    # Use with DUT
    dut.signed_input.value = sb1.binary_value
    await Timer(10, units="ns")
    
    # Arithmetic with overflow handling
    sb1.value = 127  # Maximum positive
    cocotb.log.info(f"Max positive: {sb1}")
    
    sb1.value = 128  # Wraps to -128
    cocotb.log.info(f"Overflow to: {sb1}")

Type Conversion Utilities

def to_logic_array(value, width=None):
    """Convert various types to LogicArray."""
    from cocotb.types import LogicArray, Logic
    
    if isinstance(value, str):
        # Convert binary string
        logic_bits = []
        for char in value:
            if char == '0':
                logic_bits.append(Logic.0)
            elif char == '1':
                logic_bits.append(Logic.1)
            elif char.upper() == 'X':
                logic_bits.append(Logic.X)
            elif char.upper() == 'Z':
                logic_bits.append(Logic.Z)
        return LogicArray(logic_bits)
    
    elif isinstance(value, int):
        # Convert integer to LogicArray
        if width is None:
            width = value.bit_length() if value > 0 else 1
        
        binary_str = format(value, f'0{width}b')
        return to_logic_array(binary_str)
    
    else:
        raise ValueError(f"Cannot convert {type(value)} to LogicArray")

@cocotb.test()
async def conversion_test(dut):
    """Test demonstrating type conversion utilities."""
    
    # Convert from different sources
    from_string = to_logic_array("1010X01Z")
    from_int = to_logic_array(0xAB, width=8)
    
    cocotb.log.info(f"From string: {from_string.binstr}")
    cocotb.log.info(f"From int: {from_int.binstr}")
    
    # Use converted values
    dut.test_input1.value = from_string
    dut.test_input2.value = from_int
    
    await Timer(20, units="ns")

Install with Tessl CLI

npx tessl i tessl/pypi-cocotb

docs

binary-types.md

clock-generation.md

index.md

logging-utilities.md

signal-handling.md

task-management.md

test-framework.md

triggers-timing.md

tile.json