Cocotb tips & tricks

This section consists of simple problems you may encounter when creating testbenches using cocotb/cocotbext-ndk frameworks and the solutions to those problems.

Using probes

cocotbext-ndk framework includes a base class for creating probes for various purposes. It can be found at ndk-fpga/python/cocotbext/cocotbext/ofm/base/probe.py. The probes typically read attributes of their connected agents, usually a monitor or a driver, perform calculations with them, and display the results to the terminal either at a specific time, periodically, or on request.

The base Probe class offers a couple of methods to make this possible.

To log a specific time interval, use the add_log_interval method. If you want the time interval to go to infinity, set the stop_time argument to None; however, don’t forget to also set a period, otherwise the probe will never log.

Periodic logging can be achieved by setting a period using the set_log_period method.

For cases where full control over when the probe starts and stops logging is needed, the start_log and stop_log methods are implemented.

When it comes to specific implementations of probes, at the time of writing, there is only one, and that is ThroughputProbe for measuring throughput and efficiency. Check out ndk-fpga/python/cocotbext/cocotbext/ofm/utils/throughput_probe.py for the implementation.

Class ThroughputProbe further adds the log_average_throughput and log_max_throughput methods to log the average throughput and efficiency and the maximal possible average throughput, respectively. It is recommended to call these methods at the end of the test when all the transactions have been processed.

To perform its task, a probe typically needs to read attributes from the probed agent synchronously. However, the names of the attributes of the agent may differ from the names of the attributes of the probe, or they may not be present at all; in that case, they need to be either calculated or set to a fixed value.

To resolve this, it is recommended to use interfaces derived from the ProbeInterface class. This class offers two approaches for passing a value to the probe – a translation dictionary interface_dict and properties.

The translation dictionary interface_dict should contain all needed attributes as its keys. The values linked to the keys should be either the names of the equivalent attributes of the agent, or None to indicate that the value is returned by a property of the interface.

If the value cannot be simply read from the agent object because some logic needs to be performed (e.g., the value must be calculated from multiple attributes, or the behavior depends on the type of the agent), a property with the same name as the key in interface_dict can be created, which overrides the dictionary entry.

Note

The interface always looks for a property first. Only after it is not found does it try to get the value from the connected agent using interface_dict.

For a specific implementation of a probe interface, check out the different interfaces for ThroughputProbe in ndk-fpga/python/cocotbext/cocotbext/ofm/utils/throughput_probe.py.

The setup of a probe can, for example, look like this:

self.out_throughput_probe = ThroughputProbe(
    ThroughputProbeMfbInterface(self.stream_out),
    throughput_units="bits",
    name="ThroughputProbe - OUT"
)
self.out_throughput_probe.add_log_interval(0, None)
self.out_throughput_probe.set_log_period(10)

Which then produces output like this:

#  10000.00ns INFO     cocotb.ThroughputProbe - OUT       Immediate throughput at 10.0 us: 134.5104 Gb/s, Immediate efficiency: 32.8395%
#  20000.00ns INFO     cocotb.ThroughputProbe - OUT       Immediate throughput at 20.0 us: 134.8456 Gb/s, Immediate efficiency: 32.9213%
#  30000.00ns INFO     cocotb.ThroughputProbe - OUT       Immediate throughput at 30.0 us: 134.9 Gb/s, Immediate efficiency: 32.9346%