Decoding I2C Transactions using Trace Analyzer

This notebook shows how to use the trace analyzer to capture and analyze the run-time patterns on digital I/O pins.

This notebook requires the use of Pmod TMP2 sensor. With minor changes to the notebook script, other I2C sensors can also be tested.

Step 1: Downloading the overlay

The trace analyzer for all the interfaces are exposed from the base overlay.


In [1]:
from pynq.overlays.base import BaseOverlay

overlay = BaseOverlay('base.bit')
trace_analyzer = overlay.trace_pmoda

Step 2: Setup the trace analyzer

The I2C controller on PMODA interface runs at 100KHz, it is recommended to oversample the transactions (capturing more than 2 samples per logic level). Hence here we set the sampling frequency to 400KHz.

It takes little time to capture the samples, therefore we set the number of analyzer samples to the maximum (64K samples).


In [2]:
trace_analyzer.setup(frequency_mhz=0.4, num_analyzer_samples=65535)

Step 3: Prepare the I2C sensor

We plug the PMOD TMP2 sensor to PMODA interface on the board.


In [3]:
from pynq.lib.pmod import Pmod_TMP2

sensor = Pmod_TMP2(overlay.PMODA)

Step 4: Run the trace analyzer

Start the run the trace analyzer, followed by the sensor reading the temperature values in degree C.


In [4]:
trace_analyzer.run()
reading = sensor.read()
trace_analyzer.stop()

print('Temperature is {} degree C.'.format(reading))


Temperature is 27.4 degree C.

After the trace analyzer has been stopped, we can call analyze() method to prepare all the captured samples into lanes of data. We can ignore the returned values for now because we want to see the sigrok decoded transactions as well.


In [5]:
_ = trace_analyzer.analyze()

Step 5: Analyzing and decoding

The next following cells utilize the sigrok decoders. The decoders are slightly modified, so decoded transactions can be aligned with the captured samples.

The next cell set protocol and the signal probes.

Fore more information, please read https://sigrok.org/wiki/Sigrok-cli


In [6]:
trace_analyzer.set_protocol(protocol='i2c', 
                            probes={'SCL': 'D2', 'SDA': 'D3'})

Sigrok can also show the information about a specified protocol.


In [7]:
trace_analyzer.show_protocol()


ID: i2c
Name: I²C
Long name: Inter-Integrated Circuit
Description: Two-wire, multi-master, serial bus.
License: gplv2+
Annotation classes:
- start: Start condition
- repeat-start: Repeat start condition
- stop: Stop condition
- ack: ACK
- nack: NACK
- bit: Data/address bit
- address-read: Address read
- address-write: Address write
- data-read: Data read
- data-write: Data write
- warnings: Human-readable warnings
Annotation rows:
- bits (Bits): 5 
- addr-data (Address/Data): 0 1 2 3 4 6 7 8 9 
- warnings (Warnings): 10 
Required channels:
- scl (SCL): Serial clock line
- sda (SDA): Serial data line
Optional channels:
None.
Options:
- address_format: Displayed slave address format ('shifted', 'unshifted', default 'shifted')
Documentation:
I²C (Inter-Integrated Circuit) is a bidirectional, multi-master
bus using two signals (SCL = serial clock line, SDA = serial data line).


After protocol and probes have been set, we can call the decode() method. The path to the saved samples, the starting and ending sample numbers, and the path to the decoded file have to be specified.

Note: It is recommended not to set too large number for samples to decode. A very large number of decoded samples may lead to very slow rendering of the waveform display, possibly hanging the system.

In this example, we only decode 1000 samples. In general, it is okay to decode less than 4000 samples at a time. Users can decode more than 4000 samples, but displaying the waveforms will become very slow.

If users want to check various parts of the entire trace of samples, users can set the starting and ending positions repeatedly and rerun this cell.


In [8]:
start_position, stop_position = 1, 1000
waveform_lanes = trace_analyzer.decode('pmod_i2c_trace.csv', 
                                       start_position, stop_position,
                                       'pmod_i2c_trace.pd')

Step 6: Waveform display

Let's polish the waveform for better display. We will add footer and header to the waveform dictionary.

For more information: http://wavedrom.com/tutorial.html


In [9]:
waveform_dict = {'signal': waveform_lanes,
                 'foot': {'tock': start_position},
                 'head': {'text': ['tspan', {'class': 'info h3'}, 
                                   'Pmod I2C Transactions']}}

The next cell will display the waveform. Users can use scrollbar to check the I2C transactions. The Pmod TMP2 sensor has the I2C base address 0x4B.

The next cell will take a few seconds to render the waveform.


In [10]:
from pynq.lib.logictools.waveform import draw_wavedrom

draw_wavedrom(waveform_dict)



In [11]:
from pprint import pprint

pprint(trace_analyzer.get_transactions())


[{'begin': 103, 'command': 'Start', 'end': 103},
 {'begin': 105, 'command': 'Address read: 4B', 'end': 120},
 {'begin': 122, 'command': 'ACK', 'end': 124},
 {'begin': 124, 'command': 'Data read: 0D', 'end': 140},
 {'begin': 140, 'command': 'ACK', 'end': 142},
 {'begin': 143, 'command': 'Data read: B0', 'end': 159},
 {'begin': 159, 'command': 'NACK', 'end': 161},
 {'begin': 162, 'command': 'Stop', 'end': 162},
 {'begin': 163, 'command': 'Start', 'end': 163},
 {'begin': 165, 'command': 'Address read: 4B', 'end': 179},
 {'begin': 181, 'command': 'ACK', 'end': 183},
 {'begin': 184, 'command': 'Data read: 0D', 'end': 200},
 {'begin': 200, 'command': 'ACK', 'end': 202},
 {'begin': 202, 'command': 'Data read: B0', 'end': 219},
 {'begin': 219, 'command': 'NACK', 'end': 221},
 {'begin': 222, 'command': 'Stop', 'end': 222}]

Step 7: End

Users can reset the trace analyzer; this will clear all the saved files (*.csv, *.pd, etc.).

If users want to use a different setup, reset() method also has to be called.


In [12]:
trace_analyzer.reset()