SPI decoding instance analysis of DSview

Posted by ramez_sever on Mon, 13 Jan 2020 10:13:53 +0100

SPI decoding instance analysis of DSview

Compilation process

How to compile a file if we change it?
Law 1:

  1. Compile and install our modified files
cd libsigrokdecode4DSL
sudo make install
  1. Compile dsview
cd ..
cd DSView
cmake .
  1. Run. It is recommended to enter DSview on the command line to run. In this way, you can see the print and other relevant print contents, which is convenient for finding errors.

Law two:
In / usr / local / share / libsigncode4dsl / decoders / directory, after adding, deleting and modifying files, you can directly open DSview.

Program parsing


The code of this file has only one sentence,
from .pd import Decoder
But the decoder first calls ﹐ init ﹐ py, so it introduces pd.py


First, you need the methods related to the import decoder.

import sigrokdecode as srd
from collections import namedtuple

On the important member variables and functions of the class function Decoder

1. Variables to be defined by the parent class

The following are the variables that the parent class needs to define at the beginning, many of which contain the options and comments needed to display in the front-end UI interface. (the following code contains an explanation of the important contents)

class Decoder(srd.Decoder):
    api_version = 2 # api version, pulseview is 3, DSview is 2 by default (3 is supported in version 1.00 and above)
    id = '0:spi'  
    name = '0:SPI'
    longname = 'Serial Peripheral Interface'# Long name. If there is enough space, the front end will display
    desc = 'Full-duplex, synchronous, serial bus.'
    license = 'gplv2+'
    inputs = ['logic'] # Input mode, relevant sampling number and channel level will be input into this logic
    outputs = ['spi']
    channels = (
        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
    # Channels and optional? Channels display channel notes and options to select available channels
    #The logic output signal will be output in the order of channel and optional channels, i.e. clk, miso, mosi, cs
    optional_channels = (
        {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
        {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
        {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},

    #options can add some parameters entered by users, which is convenient for the program to change according to the needs of customers, and can be used directly, such as self.options['wordsize ']
    options = (
        {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
            'values': ('active-low', 'active-high')},
        {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
            'values': (0, 1)},
        {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
            'values': (0, 1)},
        {'id': 'bitorder', 'desc': 'Bit order',
            'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
        {'id': 'wordsize', 'desc': 'Word size', 'default': 8},
    #annotations: single annotation name
    annotations = (
        ('106', 'miso-data', 'MISO data'),#0
        ('108', 'mosi-data', 'MOSI data'),#1
        ('107', 'miso-bits', 'MISO bits'),#2
        ('109', 'mosi-bits', 'MOSI bits'),#3
        ('1000', 'warnings', 'Human-readable warnings'),#4
        ('250', 'tips', 'human tips'),#5
        #(color number, id, name)
    #Annotations? Rows: comment lines whose members include their name and the above comment name number
    annotation_rows = (
        #(ID, name, (x,) x: is the number of annotations, in the order as noted above, and a comment line can have multiple comments 
        ('miso-data', 'MISO data', (0,)),
        ('miso-bits', 'MISO bits', (2,)),
        ('mosi-data', 'MOSI data', (1,)),
        ('mosi-bits', 'MOSI bits', (3,)),
        ('other', 'Other', (4, 5)),

2. Functions mainly used by decoder

Protocol decoder class function (SRD. Decoder): it mainly consists of the following important members:

__Init (self): initialize various parameters, such as CLK, CS, etc.

start(self): call this function before decoding begins. This is the location of the register () output type, checking the validity of the user provided PD options, and so on.

decode(self):(DSview1.00 and above), because there are wait () and other related methods, it is not necessary to read the values of each sampling point in a loop, and its content is the decoding logic written by the customer himself.

decode(self, ss, es, logic): (DSview0.99 and later) this is a function called by the libsigrokdecode backend, which deals with the data of logic. ss:start sample, es:end sample, logic: input quantity, including sampling number and level information.

3.logic information structure

The logic analyzer will output the sampling number and the high and low level information of each pin through logic, and the sampling order will be sorted according to the order of channel and optional channels, for example:
Set the metadata of the Decoder as follows

    channels = (
        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
    optional_channels = (
        {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
        {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},  
         {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},  

Then the tuple information received by logic is:

samplenum First set of data Second set of data ...
bit 0 1 1 0 1 0 1 0 ...
channel clk miso mosi cs clk miso mosi cs ...

Therefore, the data we receive is only the data of samplenum and bit. What is the value of bit and the corresponding order defined by our channel and optional channel can be added and modified at will.
clk and cs suggest putting the front and the back respectively.

4. output

The above has solved the problem of display and input, but how can we output to the interface after analyzing the input content? Like this:

If you want to have only one item in a single annotation row list, and in annotation rows ('miso data ',' miso data ', (0,)),

 annotation_rows = (
        #(ID, name, (x,) x: is the number of annotations, in the order as noted above 
        ('miso-data', 'MISO data', (0,)),
        ('miso-bits', 'MISO bits', (2,)),
        ('mosi-data', 'MOSI data', (3,4)),

The system provides the method of put(). For convenience, we only need to use out \
self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
Put (sampling start point, sampling end point, self.out [ANN, [No., [content]])
For a comment line list, there are multiple items, such as ('mosi data ',' MoSi data '(3,4)),
self.put(self.ss_block, es, self.out_ann, [1, ['%02X' % self.mosidata, 'data', 'd']])
Put (sampling start point, sampling end point, self.out_ann, [number, ['name ','long name','short name ']])
The number here corresponds to Number of annotation , the color can be modified in annotation.

So far, according to the above four points, it is enough for us to use as a "tool". The "method" to be parsed can be written by ourselves. If you don't know how to write it, you can continue to read the following decoding function implementation process

In the process of construction!!!!!! ×××××××××××××××××××××××××

5. About logic collection

  • DSview1.00 and above

In the new version, we add wait () and other methods to better collect samples under the conditions we need.


Wait (wait cont) means that the program blocks the wait until any element in the wait cond dictionary is satisfied, and the corresponding pin information is returned.

Pin status condition

The key in the wait cond dictionary is the key corresponding to the PD channel index (that is, the order corresponding to section 3). In this case, the value can be:
l: Pin is low
h: Pin is high
r: rising edge
f: falling edge
e: Jumping edge (rising and falling edge)
s: In contrast to the e state, there is no edge, and the current and previous pin values are both low (or both high).

Sample skip condition

self.wait({'skip':100}): return after every 100 samples
self.wait ({'skip': 20 * (1000 / self. Sample)}): returns a sample every 20 ms


When the decoder requests the front end to wait for multiple conditions through self.wait(), when the call returns, PD only knows that at least one condition has been matched. However, in most cases, it also needs to know which criteria match (or do not match).
This is the information provided by self.matched. It is a tuple of Boolean values (True or False) and always contains as many entries as the conditions that existed in the last self.wait() call. For each condition, the respective Boolean value indicates whether the particular condition matches.

pins = self.wait([{0: 'r'}, {2: 'h'}, {'skip': 1000}])
if self.matched == (True, True, False):
    print ("satisfy channel0 and channel2 And the condition of counting to 1000 sample values is not met")
if self.matched & 0b1 << 1
    print ("Only satisfy channel2 Conditions for high level")


self.samplenum is a special property that is read-only for protocol decoders and can only be set by the libsigrokdecode backend.

The value of self.samplenum is always the current absolute number of samples (starting at 0) since the last self.wait() return.

4. Output setting

We learned from that, output We need sampling start point, sampling end point and output data, so we can divide a small block to display data at the start point and end point.
So how to set it up?
Output data: first of all, data can be shifted. Take 8 bits as an example. If the data is stored from high position, we will offset the read data by 7 bits and store it in self.data, and so on. The second data is stored by 6 bits and the third data is stored by 6 bits Until the storage is completed, output self.data.
Start and end of sampling: it is officially recommended that we set up a two-dimensional list of self.bits [], the contents of which are as follows:

data ...
Serial number 0 1 2 3 4 5 ... 7
IO 1 0 0 1 0 1 ... 1
samplenum 108 107 106 105 104 103 ... 101
es 109 108 107 106 105 104 ... 102

Take an 8-bit data as an example. Every time we read a byte of data, bits.insert(0, [miso, self.samplenum, es]), that is, insert from the serial number 0, and the old value will move backward. When we finally want to output 8-bit data, set the sampling starting point to bits[-1][1], that is, set the sampling number of the bold 101 in the table, and set the sampling end point to bits[0][2], that is, set the bold 101 in the table The sample number of 109.


Finally, the coding logic is summarized as follows:
1.Define relevant parameters in advance
2. Define related member variables in "init" (self), which can be used as global variables in c.
3. Set start (self) to initialize the correlation. At this time, you can get the relevant contents of options after the conditions selected by the user.
4.decode(self) sets relevant attributes according to user information, and uses wait Set the decoder sampling conditions.
5. make use of matched Further filter the relevant information.
6. Get the corresponding data and start and end of sampling Use put Method output the corresponding data in the corresponding position.

At present, due to the limitations of version 0.99 or below, for example, when there are many channel s, it is impossible to lock more than two conditions at the same time, resulting in decoder deviation, inaccurate data, etc.
For now, it's just an introduction

Dsview under 0.99

In def decode(self, ss, es, logic): the logic contains all the sampling number and level information. We need to read them circularly. In pulseview, we encapsulate the wait() method. We can wait until a certain variable meets a certain requirement before reading.
However, in DSview, there is no wait() method, so you need to design it manually. The general principle is to save the level information of the last sampling number and the level information of the current sampling number for comparison. If there is any change, it means that the rising edge / falling edge changes. The code is as follows:

 def decode(self, ss, es, logic):
        # Either MISO or MOSI can be omitted (but not both). CS# is optional.
        for (self.samplenum, pins) in logic:
            (clk, miso, mosi, cs) = pins #The values of each level in pins are put into each variable respectively, which is described in the previous chapter
            if not self.pin_checked:
                self.have_miso = (miso in (0, 1))
                self.have_mosi = (mosi in (0, 1))
                self.have_cs = (cs in (0, 1))
                # Either MISO or MOSI (but not both) can be omitted.
                if not (self.have_miso or self.have_mosi):
                    raise ChannelError('Either MISO or MOSI (or both) pins required.')
                #This mode setting has been defined in options, that is, the customer can select cpol and cpha. For details, please refer to https://blog.csdn.net/ce123 "Zhou Wei / article / details / 6923293
                if (self.mode == 0 or self.mode == 3):
                    self.exp_oldclk = 0 
                    self.exp_clk = 1
                    self.exp_oldclk = 1
                    self.exp_clk = 0
                self.logic_mask = 0b1001 if self.have_cs else 0b0001
                self.exp_logic = 0b0000 if self.active_low else 0b1000
                self.asserted_oldcs = 1 if self.active_low else 0
                self.asserted_cs = 0 if self.active_low else 1
                self.deasserted_oldcs = 0 if self.active_low else 1
                self.deasserted_cs = 1 if self.active_low else 0
                self.pin_checked = True

            logic.logic_mask = self.logic_mask
            logic.cur_pos = self.samplenum
            logic.edge_index = -1
            #logic.itercnt += 1

            # Tell stacked decoders that we don't have a CS# signal.
            #if not self.no_cs_notification and not self.have_cs:
            #    self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
            #    self.no_cs_notification = True
            if (self.oldcs, cs) == (self.asserted_oldcs, self.asserted_cs):
                #self.ss_transfer = self.samplenum
                #self.misobytes = []
                #self.mosibytes = []
            elif (self.oldcs, cs) == (self.deasserted_oldcs, self.deasserted_cs):
                #self.put(self.ss_transfer, self.samplenum, self.out_python,
                #    ['TRANSFER', self.mosibytes, self.misobytes])
                logic.exp_logic = self.exp_logic
                cs = self.asserted_oldcs
                logic.logic_mask = 0b1000
                logic.edge_index = 3
            elif not self.have_cs or cs == self.asserted_cs:
                if (self.oldclk, clk) == (self.exp_oldclk, self.exp_clk):
                    #Sample on rising/falling clock edge
                    self.handle_bit(miso, mosi, clk, cs)   

            self.oldclk, self.oldcs = clk, cs
Published 4 original articles, won praise 0, visited 54
Private letter follow

Topics: sudo cmake