summaryrefslogtreecommitdiff
path: root/decoders/sle44xx/pd.py
blob: 775ee3c8a8fb78c9d29aa826a683e480e5da1885 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##

from common.srdhelper import bitpack_lsb
import sigrokdecode as srd

class Pin:
    RST, CLK, IO, = range(3)

class Ann:
    BIT, ATR, CMD, DATA, RESET, = range(5)

class Bin:
    SEND_DATA, = range(1)

# CMD: [annotation class index, annotation texts for zoom levels]
proto = {
    'BIT':   [Ann.BIT,   '{bit}',],
    'ATR':   [Ann.ATR,   'Answer To Reset: {data:02x}', 'ATR: {data:02x}', '{data:02x}',],
    'CMD':   [Ann.CMD,   'Command: {data:02x}', 'Cmd: {data:02x}', '{data:02x}',],
    'DATA':  [Ann.DATA,  'Data: {data:02x}', '{data:02x}',],
    'RESET': [Ann.RESET, 'Reset', 'R',],
}

def lookup_proto_ann_txt(cmd, variables):
    ann = proto.get(cmd, None)
    if ann is None:
        return None, []
    cls, texts = ann[0], ann[1:]
    texts = [t.format(**variables) for t in texts]
    return cls, texts

class Decoder(srd.Decoder):
    api_version = 3
    id = 'sle44xx'
    name = 'SLE 44xx'
    longname = 'SLE44xx memory card'
    desc = 'SLE 4418/28/32/42 memory card serial protocol'
    license = 'gplv2+'
    inputs = ['logic']
    outputs = []
    tags = ['Memory']
    channels = (
        {'id': 'rst', 'name': 'RST', 'desc': 'Reset line'},
        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'},
        {'id': 'io', 'name': 'I/O', 'desc': 'I/O data line'},
    )
    annotations = (
        ('bit', 'Bit'),
        ('atr', 'ATR'),
        ('cmd', 'Command'),
        ('data', 'Data exchange'),
        ('reset', 'Reset'),
    )
    annotation_rows = (
        ('bits', 'Bits', (Ann.BIT,)),
        ('fields', 'Fields', (Ann.ATR, Ann.CMD, Ann.DATA)),
        ('interrupts', 'Interrupts', (Ann.RESET,)),
    )
    binary = (
        ('send-data', 'Send data'),
    )

    def __init__(self):
        self.reset()

    def reset(self):
        self.ss = self.es = self.ss_byte = -1
        self.bits = []
        self.cmd = None

    def metadata(self, key, value):
        if key == srd.SRD_CONF_SAMPLERATE:
            self.samplerate = value

    def start(self):
        self.out_ann = self.register(srd.OUTPUT_ANN)
        self.out_binary = self.register(srd.OUTPUT_BINARY)

    def putx(self, data):
        self.put(self.ss, self.es, self.out_ann, data)

    def putb(self, data):
        self.put(self.ss, self.es, self.out_binary, data)

    def handle_reset(self, pins):
        self.ss, self.es = self.samplenum, self.samplenum
        self.cmd = 'RESET'
        cls, texts = lookup_proto_ann_txt(self.cmd, {})
        self.putx([cls, texts])
        self.bits = []
        # Next data bytes will be Answer To Reset.
        self.cmd = 'ATR'

    def handle_command(self, pins):
        rst, clk, io = pins
        self.ss, self.es = self.samplenum, self.samplenum
        # XXX Is the comment inverted?
        # If I/O is rising -> command START
        # if I/O is falling -> command STOP and response data incoming
        self.cmd = 'CMD' if io == 0 else 'DATA'
        self.bits = []

    # Gather 8 bits of data
    def handle_data(self, pins):
        rst, clk, io = pins

        # Remember the start of the first data/address bit. Collect
        # bits in LSB first order. "Estimate" the bit's width at first,
        # update end times as better data becomes available.
        # TODO This estimation logic is imprecise and fragile. A single
        # slightly stretched clock period throws off the following bit
        # annotation. Better look for more reliable conditions. Available
        # documentation suggests bit values are valid during high CLK.
        if not self.bits:
            self.ss_byte = self.samplenum
        bit_val = io
        bit_ss = self.samplenum
        bit_es = bit_ss # self.bitwidth is not known yet.
        if self.bits:
            self.bits[-1][2] = bit_ss
        self.bits.append([bit_val, bit_ss, bit_es])
        if len(self.bits) < 8:
            return
        bitwidth = self.bits[-1][1] - self.bits[-2][1]
        self.bits[-1][2] += bitwidth

        # Get the data byte value, and byte's ss/es.
        databyte = bitpack_lsb(self.bits, 0)
        self.ss_byte = self.bits[0][1]
        self.es_byte = self.bits[-1][2]

        self.ss, self.es = self.ss_byte, self.es_byte
        self.putb([Bin.SEND_DATA, bytes([databyte])])

        # TODO Present bit values earlier. As soon as their es is known.
        for bit_val, bit_ss, bit_es in self.bits:
            cls, texts = lookup_proto_ann_txt('BIT', {'bit': bit_val})
            self.put(bit_ss, bit_es, self.out_ann, [cls, texts])

        cls, texts = lookup_proto_ann_txt(self.cmd, {'data': databyte})
        if cls:
            self.putx([cls, texts])

        # Done with this packet.
        self.bits = []

    def decode(self):
        while True:
            # Signal conditions tracked by the protocol decoder:
            # - RESET condition (R): RST = rising
            # - Incoming data (D): RST = low, CLK = rising.
            #   TODO Add "RST low, CLK fall" for "end of DATA" here?
            # - Command mode START: CLK = high, I/O = falling.
            # - Command mode STOP: CLK = high, I/O = rising.
            (COND_RESET, COND_DATA, COND_CMD_START, COND_CMD_STOP,) = range(4)
            conditions = [
                {Pin.RST: 'r'},
                {Pin.RST: 'l', Pin.CLK: 'r'},
                {Pin.CLK: 'h', Pin.IO: 'f'},
                {Pin.CLK: 'h', Pin.IO: 'r'},
            ]
            pins = self.wait(conditions)
            if self.matched[COND_RESET]:
                self.handle_reset(pins)
            elif self.matched[COND_DATA]:
                self.handle_data(pins)
            elif self.matched[COND_CMD_START]:
                self.handle_command(pins)
            elif self.matched[COND_CMD_STOP]:
                self.handle_command(pins)