Difference between revisions of "Protocol decoder HOWTO"

From sigrok
Jump to navigation Jump to search
m
(Update, extend.)
Line 9: Line 9:
== Files ==
== Files ==


* __init_.py
Every protocol decoder is a Python module and has its own subdirectory in libsigrokdecode's '''decoders''' directory. The following files are required for a decoder:
* ''<decodername>''.py
 
* ...
* '''Makefile.am''': File for '''automake''', so the build system knows about the PD and the files belonging to it.
* '''__init__.py''': A standard Python file, required in every Python module.
* '''pd.py''': Recommended file name for the main protocol decoder code.


== Protocol decoder skeleton ==
== Protocol decoder skeleton ==


This is a minimalistic framework/example for a sigrok protocol decoder (license header, comments, and some other parts omitted):
This is a minimalistic example of how a sigrok protocol decoder looks like, in this case the '''i2c''' decoder (license header, comments, and some other parts omitted).
 
'''Note''': Do not start new protocol decoders by copying code from here. Instead, it's recommended to select an already existing decoder in the source code which is similar to the one you plan to write, and copy that as a starting point.
 
=== Makefile.am ===


<small>
<small>
<pre>
<pre>
  import sigrokdecode as srd
  pkgdatadir = $(DECODERS_DIR)/i2c
dist_pkgdata_DATA = __init__.py i2c.py
CLEANFILES = *.pyc
</pre>
</small>
 
This file is used to tell the build system which files belong to the decoder.
 
=== __init__.py ===
 
<small>
<pre>
'''
I2C protocol decoder.
The Inter-Integrated Circuit (I2C) bus is a bidirectional, multi-master
bus using two signals (SCL = serial clock line, SDA = serial data line).
   
   
<Insert further descriptions etc. here>
  '''
  '''
  <Module-level docstring, accessible by frontends via the libsigrokdecode API>
   
'''
from .i2c import *
</pre>
</small>
 
This file contains a module-level docstring, which is accessible by frontends via the libsigrokdecode API. It should contain a description of the protocol (in this case I2C), and some protocol decoder info, such as its options, protocol output format, and so on.
 
The "'''from .i2c import *'''" line will make sure all '''*.py''' files in the decoder's directory will be properly imported when this module is used.
 
=== pd.py ===
 
<small>
<pre>
import sigrokdecode as srd
   
   
  class Decoder(srd.Decoder):
  class Decoder(srd.Decoder):
Line 30: Line 65:
     name = 'I2C'
     name = 'I2C'
     longname = 'Inter-Integrated Circuit'
     longname = 'Inter-Integrated Circuit'
     desc = '<Short, one-line description>'
     desc = 'Two-wire, multi-master, serial bus.'
    longdesc = '<Long, multi-line description>'
     license = 'gplv2+'
     license = 'gplv2+'
     inputs = ['logic']
     inputs = ['logic']
Line 44: Line 78:
     }
     }
     annotations = [
     annotations = [
         ['ASCII', 'Annotations in ASCII format'],
         ['7-bit shifted hex', 'Read/write bit shifted out from the 8-bit I2C slave address'],
        ['7-bit shifted hex (short)', 'Read/write bit shifted out from the 8-bit I2C slave address'],
        ['Raw hex', 'Unaltered raw data'],
     ]
     ]
   
   
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         self.state = 'IDLE'
         self.state = 'FIND START'
        self.samplenum = 0
   
   
     def start(self, metadata):
     def start(self, metadata):
        self.samplerate = metadata['samplerate']
         self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
         self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
         self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
         self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
   
   
     def report(self):
     def report(self):
         # ...
         pass # Unused so far.
   
   
     def decode(self, ss, es, data):
     def decode(self, ss, es, data):
         for samplenum, (scl, sda) in data:
         for self.samplenum, (scl, sda) in data:
             # ...
             # ...
</pre>
</pre>
</small>
</small>
This file contains some meta information about the decoder, and the actual code itself, mostly in the '''decode()''' method.


== Random notes, tips and tricks ==
== Random notes, tips and tricks ==

Revision as of 17:03, 22 July 2012

This page serves as a quick-start guide for people who want to write their own sigrok protocol decoders (PDs).

It is not intended to replace the Protocol decoder API page, but rather to give a short overview/tutorial and some tips.

Introduction

TODO.

Files

Every protocol decoder is a Python module and has its own subdirectory in libsigrokdecode's decoders directory. The following files are required for a decoder:

  • Makefile.am: File for automake, so the build system knows about the PD and the files belonging to it.
  • __init__.py: A standard Python file, required in every Python module.
  • pd.py: Recommended file name for the main protocol decoder code.

Protocol decoder skeleton

This is a minimalistic example of how a sigrok protocol decoder looks like, in this case the i2c decoder (license header, comments, and some other parts omitted).

Note: Do not start new protocol decoders by copying code from here. Instead, it's recommended to select an already existing decoder in the source code which is similar to the one you plan to write, and copy that as a starting point.

Makefile.am

 pkgdatadir = $(DECODERS_DIR)/i2c
 dist_pkgdata_DATA = __init__.py i2c.py
 CLEANFILES = *.pyc

This file is used to tell the build system which files belong to the decoder.

__init__.py

 '''
 I2C protocol decoder.
 
 The Inter-Integrated Circuit (I2C) bus is a bidirectional, multi-master
 bus using two signals (SCL = serial clock line, SDA = serial data line).
 
 <Insert further descriptions etc. here>
 '''
 
 from .i2c import *

This file contains a module-level docstring, which is accessible by frontends via the libsigrokdecode API. It should contain a description of the protocol (in this case I2C), and some protocol decoder info, such as its options, protocol output format, and so on.

The "from .i2c import *" line will make sure all *.py files in the decoder's directory will be properly imported when this module is used.

pd.py

 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
     api_version = 1
     id = 'i2c'
     name = 'I2C'
     longname = 'Inter-Integrated Circuit'
     desc = 'Two-wire, multi-master, serial bus.'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['i2c']
     probes = [
         {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
         {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
     ]
     optional_probes = []
     options = {
         'addressing': ['Slave address size (in bits)', 7],
     }
     annotations = [
         ['7-bit shifted hex', 'Read/write bit shifted out from the 8-bit I2C slave address'],
         ['7-bit shifted hex (short)', 'Read/write bit shifted out from the 8-bit I2C slave address'],
         ['Raw hex', 'Unaltered raw data'],
     ]
 
     def __init__(self, **kwargs):
         self.state = 'FIND START'
         self.samplenum = 0
 
     def start(self, metadata):
         self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
         self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
 
     def report(self):
         pass # Unused so far.
 
     def decode(self, ss, es, data):
         for self.samplenum, (scl, sda) in data:
             # ...

This file contains some meta information about the decoder, and the actual code itself, mostly in the decode() method.

Random notes, tips and tricks

  • You should only use raise in a protocol decoder to raise exceptions in cases which are a clear bug in the protocol decoder.
  • A simple and fast way to calculate a parity (i.e., count the number of 1 bits) over a number (0x55 in this example) is: ones = bin(0x55).count('1').
  • A simple function to convert a BCD number (max. 8 bits) to an integer is: def bcd2int(b): return (b & 0x0f) + ((b >> 4) * 10).
  • A nice way to construct method names according to (e.g.) protocol commands is: fn = getattr(self, 'handle_cmd_0x%02x' % cmd); fn(arg1, arg2, ...).