]> sigrok.org Git - libsigrokdecode.git/blob - decoders/sle44xx/pd.py
a53a1ea18ebc9f543ec4429536e5664450ec0653
[libsigrokdecode.git] / decoders / sle44xx / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
18 ##
19
20 from common.srdhelper import bitpack_lsb
21 import sigrokdecode as srd
22
23 class Pin:
24     RST, CLK, IO, = range(3)
25
26 class Ann:
27     BIT, ATR, CMD, DATA, RESET, = range(5)
28
29 class Bin:
30     SEND_DATA, = range(1)
31
32 # CMD: [annotation class index, annotation texts for zoom levels]
33 proto = {
34     'BIT':   [Ann.BIT,   '{bit}',],
35     'ATR':   [Ann.ATR,   'Answer To Reset: {data:02x}', 'ATR: {data:02x}', '{data:02x}',],
36     'CMD':   [Ann.CMD,   'Command: {data:02x}', 'Cmd: {data:02x}', '{data:02x}',],
37     'DATA':  [Ann.DATA,  'Data: {data:02x}', '{data:02x}',],
38     'RESET': [Ann.RESET, 'Reset', 'R',],
39 }
40
41 def lookup_proto_ann_txt(cmd, variables):
42     ann = proto.get(cmd, None)
43     if ann is None:
44         return None, []
45     cls, texts = ann[0], ann[1:]
46     texts = [t.format(**variables) for t in texts]
47     return cls, texts
48
49 class Decoder(srd.Decoder):
50     api_version = 3
51     id = 'sle44xx'
52     name = 'SLE 44xx'
53     longname = 'SLE44xx memory card'
54     desc = 'SLE 4418/28/32/42 memory card serial protocol'
55     license = 'gplv2+'
56     inputs = ['logic']
57     outputs = []
58     tags = ['Memory']
59     channels = (
60         {'id': 'rst', 'name': 'RST', 'desc': 'Reset line'},
61         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'},
62         {'id': 'io', 'name': 'I/O', 'desc': 'I/O data line'},
63     )
64     annotations = (
65         ('bit', 'Bit'),
66         ('atr', 'ATR'),
67         ('cmd', 'Command'),
68         ('data', 'Data exchange'),
69         ('reset', 'Reset'),
70     )
71     annotation_rows = (
72         ('bits', 'Bits', (Ann.BIT,)),
73         ('fields', 'Fields', (Ann.ATR, Ann.CMD, Ann.DATA)),
74         ('interrupts', 'Interrupts', (Ann.RESET,)),
75     )
76     binary = (
77         ('send-data', 'Send data'),
78     )
79
80     def __init__(self):
81         self.reset()
82
83     def reset(self):
84         self.ss = self.es = self.ss_byte = -1
85         self.bits = []
86         self.cmd = 'RESET'
87
88     def metadata(self, key, value):
89         if key == srd.SRD_CONF_SAMPLERATE:
90             self.samplerate = value
91
92     def start(self):
93         self.out_ann = self.register(srd.OUTPUT_ANN)
94         self.out_binary = self.register(srd.OUTPUT_BINARY)
95
96     def putx(self, data):
97         self.put(self.ss, self.es, self.out_ann, data)
98
99     def putb(self, data):
100         self.put(self.ss, self.es, self.out_binary, data)
101
102     def handle_reset(self, pins):
103         self.ss, self.es = self.samplenum, self.samplenum
104         self.cmd = 'RESET'
105         cls, texts = lookup_proto_ann_txt(self.cmd, {})
106         self.putx([cls, texts])
107         self.bits = []
108         self.cmd = 'ATR' # Next data bytes will be ATR
109
110     def handle_command(self, pins):
111         rst, clk, io = pins
112         self.ss, self.es = self.samplenum, self.samplenum
113         # If I/O is rising -> command START
114         # if I/O is falling -> command STOP and response data incoming
115         self.cmd = 'CMD' if (io == 0) else 'DATA'
116         self.bits = []
117
118     # Gather 8 bits of data
119     def handle_data(self, pins):
120         rst, clk, io = pins
121
122         # Remember the start of the first data/address bit. Collect
123         # bits in LSB first order. "Estimate" the bit's width at first,
124         # update end times as better data becomes available.
125         # TODO This estimation logic is imprecise and fragile. A single
126         # slightly stretched clock period throws off the following bit
127         # annotation. Better look for more reliable conditions. Available
128         # documentation suggests bit values are valid during high CLK.
129         if not self.bits:
130             self.ss_byte = self.samplenum
131         bit_val = io
132         bit_ss = self.samplenum
133         bit_es = bit_ss # self.bitwidth is not known yet.
134         if self.bits:
135             self.bits[-1][2] = bit_ss
136         self.bits.append([bit_val, bit_ss, bit_es])
137         if len(self.bits) < 8:
138             return
139         bitwidth = self.bits[-1][1] - self.bits[-2][1]
140         self.bits[-1][2] += bitwidth
141
142         # Get the data byte value, and byte's ss/es.
143         databyte = bitpack_lsb(self.bits, 0)
144         self.ss_byte = self.bits[0][1]
145         self.es_byte = self.bits[-1][2]
146
147         self.ss, self.es = self.ss_byte, self.es_byte
148         self.putb([Bin.SEND_DATA, bytes([databyte])])
149
150         # TODO Present bit values earlier. As soon as their es is known.
151         for bit_val, bit_ss, bit_es in self.bits:
152             cls, texts = lookup_proto_ann_txt('BIT', {'bit': bit_val})
153             self.put(bit_ss, bit_es, self.out_ann, [cls, texts])
154
155         cls, texts = lookup_proto_ann_txt(self.cmd, {'data': databyte})
156         self.putx([cls, texts])
157
158         # Done with this packet.
159         self.bits = []
160
161     def decode(self):
162         while True:
163             # Signal conditions tracked by the protocol decoder:
164             # - RESET condition (R): RST = rising
165             # - Incoming data (D): RST = low, CLK = rising.
166             #   TODO Add "RST low, CLK fall" for "end of DATA" here?
167             # - Command mode START: CLK = high, I/O = falling.
168             # - Command mode STOP: CLK = high, I/O = rising.
169             (COND_RESET, COND_DATA, COND_CMD_START, COND_CMD_STOP,) = range(4)
170             conditions = [
171                 {Pin.RST: 'r'},
172                 {Pin.RST: 'l', Pin.CLK: 'r'},
173                 {Pin.CLK: 'h', Pin.IO: 'f'},
174                 {Pin.CLK: 'h', Pin.IO: 'r'},
175             ]
176             pins = self.wait(conditions)
177             if self.matched[COND_RESET]:
178                 self.handle_reset(pins)
179             elif self.matched[COND_DATA]:
180                 self.handle_data(pins)
181             elif self.matched[COND_CMD_START]:
182                 self.handle_command(pins)
183             elif self.matched[COND_CMD_STOP]:
184                 self.handle_command(pins)