libsigrokdecode: decoding SOP*'s from 3 k-codes; text output.

Allow detailed decoding of SOP, SOP', SOP", SOP'_Debug, SOP"_Debug.
Allow decoding SOP*, Cable Reset, Hard Reset from 3 valid k-codes,
(instead of all 4), which is specified in the USB PD spec.

Allow text output without running PulseView.

Signed-off-by: Dawei Li <daweili@google.com>

BUG=chrome-os-partner:42703
TEST=BEGIN
The following command will output the pd packets in text format:
sigrok-cli -i test.sr -P usb_pd_bmc:cc=CC1,usb_pd_packet -A usb_pd_packet=text

Change CC1 to CC2, if that is the case.
END

Change-Id: I18ca145b9be5c28770b3ab7c42c2783008dc8754
Reviewed-on: https://chromium-review.googlesource.com/289249
Commit-Ready: Sheng-liang Song <ssl@chromium.org>
Tested-by: Dawei Li <daweili@google.com>
Reviewed-by: Sheng-liang Song <ssl@chromium.org>
diff --git a/decoders/usb_pd_bmc/pd.py b/decoders/usb_pd_bmc/pd.py
index 67356f9..f0f6e23 100644
--- a/decoders/usb_pd_bmc/pd.py
+++ b/decoders/usb_pd_bmc/pd.py
@@ -64,6 +64,7 @@
         self.edges = []
         self.half_one = False
         self.start_one = 0
+        self.tstamp = 0
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -118,7 +119,7 @@
                 self.edges.append(self.previous)
                 # Export the packet
                 self.putx([0, ['BMC %d' % (self.seq),'PD']])
-                self.putp({'BITS': self.packet, 'EDGES':self.edges})
+                self.putp({'TIMESTAMP':tstamp, 'BITS': self.packet, 'EDGES':self.edges})
                 #self.putb((1, bytes(self.packet))
                 # Reset for next packet
                 self.startsample = self.samplenum
@@ -149,4 +150,3 @@
                     self.edges.append(self.previous)
                     self.half_one = False
             self.previous = self.samplenum
-
diff --git a/decoders/usb_pd_packet/pd.py b/decoders/usb_pd_packet/pd.py
index 5eb7af2..c1c0fdf 100644
--- a/decoders/usb_pd_packet/pd.py
+++ b/decoders/usb_pd_packet/pd.py
@@ -95,6 +95,17 @@
 SYNC_CODES=[SYNC1, SYNC2, SYNC3]
 HRST_CODES=[RST1, RST1, RST1, RST2]
 
+# different SOPs: SOP, SOP', SOP", SOP'_Debug, SOP"_Debug
+SOP        =  [SYNC1, SYNC1, SYNC1, SYNC2, "SOP" ]
+SOPP       =  [SYNC1, SYNC1, SYNC3, SYNC3, "SOP'"]
+SOPPP      =  [SYNC1, SYNC3, SYNC1, SYNC3, 'SOP"']
+SOPPDebug  =  [SYNC1,  RST2,  RST2, SYNC3, "SOP'_Debug"]
+SOPPPDebug =  [SYNC1,  RST2, SYNC3, SYNC2, 'SOP"_Debug']
+START_OF_PACKETS = [SOP, SOPP, SOPPP, SOPPDebug, SOPPPDebug]
+
+CableReset =  [ RST1, SYNC1,  RST1, SYNC3, "Cable Reset"]
+HardReset  =  [ RST1,  RST1,  RST1,  RST2, "Hard Reset" ]
+
 SYM_NAME = [
     ['0x0', '0'],
     ['0x1', '1'],
@@ -366,18 +377,40 @@
         hi = self.get_short()
         return lo | (hi << 16)
 
+    def is_hard_reset(self, sym_array):
+        first = 1 if sym_array[0]==HardReset[0] else 0
+        second = 1 if sym_array[1]==HardReset[1] else 0
+        third = 1 if sym_array[2]==HardReset[2] else 0
+        fourth = 1 if sym_array[3]==HardReset[3] else 0
+        return first + second + third + fourth >= 3
+
+    def is_cable_reset(self, sym_array):
+        first = 1 if sym_array[0]==CableReset[0] else 0
+        second = 1 if sym_array[1]==CableReset[1] else 0
+        third = 1 if sym_array[2]==CableReset[2] else 0
+        fourth = 1 if sym_array[3]==CableReset[3] else 0
+        return first + second + third + fourth >= 3
+
+    def get_start_of_packet(self, sym_array):
+        for i in range(5):
+            first = 1 if sym_array[0]==START_OF_PACKETS[i][0] else 0
+            second = 1 if sym_array[1]==START_OF_PACKETS[i][1] else 0
+            third = 1 if sym_array[2]==START_OF_PACKETS[i][2] else 0
+            fourth = 1 if sym_array[3]==START_OF_PACKETS[i][3] else 0
+            if first + second + third + fourth >= 3:
+                return START_OF_PACKETS[i][4]
+            else:
+                return None
+
     def scan_eop(self):
         for i in range(len(self.bits) - 19):
-            k = [self.get_sym(i, rec=False), self.get_sym(i+5, rec=False), self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False)]
-            syncs = 0
-            hreset = HRST_CODES[:]
-            for sym in k:
-                if sym in SYNC_CODES:
-                    syncs += 1
-                if sym == hreset[0]:
-                    del hreset[0]
+            k = [self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
+                 self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False)]
+            hard_reset = self.is_hard_reset(k)
+            cable_reset = self.is_cable_reset(k)
+            start_of_packet = self.get_start_of_packet(k)
             # We have an interesting symbol sequence
-            if len(hreset) == 0 or syncs >= 4: #TODO 3 symbols
+            if hard_reset or cable_reset or start_of_packet!=None:
                 # annotate the preamble
                 self.putx(0, i, [1, ['Preamble', '...']])
                 # annotate each symbol
@@ -385,14 +418,13 @@
                 self.rec_sym(i+5, k[1])
                 self.rec_sym(i+10, k[2])
                 self.rec_sym(i+15, k[3])
-                # we have the 4 HardReset symbols
-                if len(hreset) == 0:
-                    # annote the HardReset
-                    self.putx(i, i+20, [2, ['HardReset', 'HRST']])
+                if hard_reset:
                     self.text += "HRST"
                     return HARD_RESET
-                # 3 valid SYNC-x codes : SOP*
-                self.putx(i, i+20, [2, ['SOP*', 'SOP']])
+                elif cable_reset:
+                    self.putx(i, i+20, [2, [CableReset[4], 'CRST']])
+                else:
+                    self.putx(i, i+20, [2, [start_of_packet, 'S']])
                 return i+20
         self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
         self.text += "Junk???"
@@ -402,6 +434,7 @@
     def __init__(self, **kwargs):
         self.samplerate = None
         self.idx = 0
+        self.packet_seq = 0
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -422,19 +455,18 @@
             print(traceback.format_exc())
 
     def decode_(self, ss, es, data):
+        self.tstamp = data['TIMESTAMP']
         self.bits = data['BITS']
         self.edges = data['EDGES']
         self.data = []
         self.idx = 0
+        self.samplenum = 0
 
         if len(self.edges) < 50:
             return # Not a real PD packet
 
-        # TODO:Samplerate metadata not passed to stacked decoder : HARDCODING
-        self.samplerate = 2400000
-        t0_time = self.edges[0]/self.samplerate
-        dt_us = (self.edges[-1] - self.edges[0])/(self.samplerate/1000000.0)
-        self.text = "%5.6f (%4d): " % (t0_time, dt_us)
+        self.packet_seq += 1
+        self.text = "#%d (%8.6fms): " % (self.packet_seq, self.tstamp*1000)
 
         self.idx = self.scan_eop()
         if self.idx < 0:
@@ -466,6 +498,5 @@
             self.putx(self.idx-5, self.idx, [6, ['EOP', 'E']])
         else:
             self.putwarn("No EOP","EOP!")
-
         # Full text trace
         self.putx(0, self.idx, [12, [self.text, 'TXT']])