Trace Buffer - Tracing SPI Transactions

The Trace_Buffer class can monitor the waveform and transations on PMODA, PMODB, and ARDUINO connectors.

This demo shows how to use this class to track SPI transactions. For this demo, users have to connect the Pmod OLED to PMODB.

Step 1: Overlay Management

Users have to import all the necessary classes. Make sure to use the right bitstream.


In [1]:
from pprint import pprint
from time import sleep
from pynq import PL
from pynq import Overlay
from pynq.drivers import Trace_Buffer
from pynq.iop import Pmod_OLED
from pynq.iop import PMODA
from pynq.iop import PMODB
from pynq.iop import ARDUINO

ol = Overlay("base.bit")
ol.download()
pprint(PL.ip_dict)


{'SEG_axi_dma_0_Reg': ['0x80400000', '0x00010000', None],
 'SEG_axi_dma_0_Reg1': ['0x80410000', '0x00010000', None],
 'SEG_axi_dynclk_0_reg0': ['0x43C10000', '0x00010000', None],
 'SEG_axi_gpio_video_Reg': ['0x41220000', '0x00010000', None],
 'SEG_axi_vdma_0_Reg': ['0x43000000', '0x00010000', None],
 'SEG_btns_gpio_Reg': ['0x41210000', '0x00010000', None],
 'SEG_d_axi_pdm_1_S_AXI_reg': ['0x43C00000', '0x00010000', None],
 'SEG_hdmi_out_hpd_video_Reg': ['0x41230000', '0x00010000', None],
 'SEG_mb_bram_ctrl_1_Mem0': ['0x40000000', '0x00010000', None],
 'SEG_mb_bram_ctrl_2_Mem0': ['0x42000000', '0x00010000', None],
 'SEG_mb_bram_ctrl_3_Mem0': ['0x44000000', '0x00010000', None],
 'SEG_rgbled_gpio_Reg': ['0x41240000', '0x00010000', None],
 'SEG_swsleds_gpio_Reg': ['0x41200000', '0x00010000', None],
 'SEG_trace_cntrl_0_Reg': ['0x83C00000', '0x00010000', None],
 'SEG_trace_cntrl_0_Reg2': ['0x83C10000', '0x00010000', None],
 'SEG_v_tc_0_Reg': ['0x43C20000', '0x00010000', None],
 'SEG_v_tc_1_Reg': ['0x43C30000', '0x00010000', None]}

Step 2: Instantiating OLED

Although this demo can also be done on PMODA, we use PMODB in this demo.


In [2]:
oled = Pmod_OLED(PMODB)

Step 3: Tracking Transactions

Instantiating the trace buffer with SPI protocol. The SPI clock is controlled by the 100MHz IO Processor (IOP). The SPI clock period is 16 times the IOP clock rate based on the settings of the IOP SPI controller. Hence we set the sample rate to 20MHz.

After starting the trace buffer DMA, also start to write some characters. Then stop the trace buffer DMA.


In [3]:
tr_buf = Trace_Buffer(PMODB,"spi",samplerate=20000000)

# Start the trace buffer
tr_buf.start()

# Write characters
oled.write("1 2 3 4 5 6")

# Stop the trace buffer
tr_buf.stop()

Step 4: Parsing and Decoding Transactions

The trace buffer object is able to parse the transactions into a *.csv file (saved into the same folder as this script). The input arguments for the parsing method is:

* start : the starting sample number of the trace.
* stop : the stopping sample number of the trace.
* tri_sel: masks for tri-state selection bits.
* tri_0: masks for pins selected when the corresponding tri_sel = 0.
* tri_0: masks for pins selected when the corresponding tri_sel = 1.
* mask: mask for pins selected always.

For PMODA, the configuration of the masks can be:

* tri_sel = [0x80000,0x40000,0x20000,0x10000]
* tri_0 = [0x8,0x4,0x2,0x1]
* tri_1 = [0x800,0x400,0x200,0x100]
* mask = 0x0

Then the trace buffer object can also decode the transactions using the open-source sigrok decoders. The decoded file (*.pd) is saved into the same folder as this script.

Reference: https://sigrok.org/wiki/Main_Page


In [4]:
# Configuration for PMODB
start = 20000
stop = 40000
tri_sel = [0x80000<<32,0x40000<<32,0x20000<<32,0x10000<<32]
tri_0 = [0x8<<32,0x4<<32,0x2<<32,0x1<<32]
tri_1 = [0x800<<32,0x400<<32,0x200<<32,0x100<<32]
mask = 0x0

# Parsing and decoding
tr_buf.parse("spi_trace.csv",
             start,stop,mask,tri_sel,tri_0,tri_1)
tr_buf.set_metadata(['CLK','NC','MOSI','CS'])
tr_buf.decode("spi_trace.pd",
              options=':wordsize=8:cpol=0:cpha=0')

Step 5: Displaying the Result

The final waveform and decoded transactions are shown using the open-source wavedrom library. The two input arguments (s0 and s1 ) indicate the starting and stopping location where the waveform is shown.

The valid range for s0 and s1 is: 0 < s0 < s1 < (stop-start), where start and stop are defined in the last step.

Reference: https://www.npmjs.com/package/wavedrom


In [5]:
s0 = 10000
s1 = 15000
tr_buf.display(s0,s1)