]> sigrok.org Git - libsigrokdecode.git/blob - decoders/mdio/pd.py
decoders: Add/update tags for each PD.
[libsigrokdecode.git] / decoders / mdio / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
5 ## All rights reserved.
6 ##
7 ## Redistribution and use in source and binary forms, with or without
8 ## modification, are permitted provided that the following conditions are met:
9 ##
10 ## 1. Redistributions of source code must retain the above copyright notice,
11 ##    this list of conditions and the following disclaimer.
12 ## 2. Redistributions in binary form must reproduce the above copyright notice,
13 ##    this list of conditions and the following disclaimer in the documentation
14 ##    and/or other materials provided with the distribution.
15 ##
16 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 ## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 ## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 ## POSSIBILITY OF SUCH DAMAGE.
27 ##
28
29 import sigrokdecode as srd
30
31 class Decoder(srd.Decoder):
32     api_version = 3
33     id = 'mdio'
34     name = 'MDIO'
35     longname = 'Management Data Input/Output'
36     desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
37     license = 'bsd'
38     inputs = ['logic']
39     outputs = ['mdio']
40     tags = ['Networking']
41     channels = (
42         {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
43         {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
44     )
45     options = (
46         {'id': 'show_debug_bits', 'desc': 'Show debug bits',
47             'default': 'no', 'values': ('yes', 'no')},
48     )
49     annotations = (
50         ('bit-val', 'Bit value'),
51         ('bit-num', 'Bit number'),
52         ('frame', 'Frame'),
53         ('frame-idle', 'Bus idle state'),
54         ('frame-error', 'Frame error'),
55         ('decode', 'Decode'),
56     )
57     annotation_rows = (
58         ('bit-val', 'Bit value', (0,)),
59         ('bit-num', 'Bit number', (1,)),
60         ('frame', 'Frame', (2, 3)),
61         ('frame-error', 'Frame error', (4,)),
62         ('decode', 'Decode', (5,)),
63     )
64
65     def __init__(self):
66         self.reset()
67
68     def reset(self):
69         self.illegal_bus = 0
70         self.samplenum = -1
71         self.clause45_addr = -1 # Clause 45 is context sensitive.
72         self.reset_decoder_state()
73
74     def start(self):
75         self.out_python = self.register(srd.OUTPUT_PYTHON)
76         self.out_ann = self.register(srd.OUTPUT_ANN)
77
78     def putbit(self, mdio, ss, es):
79         self.put(ss, es, self.out_ann, [0, ['%d' % mdio]])
80         if self.options['show_debug_bits'] == 'yes':
81             self.put(ss, es, self.out_ann, [1, ['%d' % (self.bitcount - 1), '%d' % ((self.bitcount - 1) % 10)]])
82
83     def putff(self, data):
84         self.put(self.ss_frame_field, self.samplenum, self.out_ann, data)
85
86     def putdata(self):
87         self.put(self.ss_frame_field, self.mdiobits[0][2], self.out_ann,
88                  [2, ['DATA: %04X' % self.data, 'DATA', 'D']])
89
90         if self.clause45 and self.opcode == 0:
91             self.clause45_addr = self.data
92
93         # Decode data.
94         if self.opcode > 0 or not self.clause45:
95             decoded_min = ''
96             if self.clause45 and self.clause45_addr != -1:
97                 decoded_min += str.format('ADDR: %04X ' % self.clause45_addr)
98             elif self.clause45:
99                 decoded_min += str.format('ADDR: UKWN ')
100
101             if self.clause45 and self.opcode > 1 \
102             or (not self.clause45 and self.opcode):
103                 decoded_min += str.format('READ:  %04X' % self.data)
104                 is_read = 1
105             else:
106                 decoded_min += str.format('WRITE: %04X' % self.data)
107                 is_read = 0
108             decoded_ext = str.format(' %s: %02d' % \
109                         ('PRTAD' if self.clause45 else 'PHYAD', self.portad))
110             decoded_ext += str.format(' %s: %02d' % \
111                         ('DEVAD' if self.clause45 else 'REGAD', self.devad))
112             if self.ta_invalid or self.op_invalid:
113                 decoded_ext += ' ERROR'
114             self.put(self.ss_frame, self.mdiobits[0][2], self.out_ann,
115                      [5, [decoded_min + decoded_ext, decoded_min]])
116
117             self.put(self.ss_frame, self.mdiobits[0][2], self.out_python,
118                      [(bool(self.clause45), int(self.clause45_addr), \
119                        bool(is_read), int(self.portad), int(self.devad), \
120                        int(self.data))])
121
122         # Post read increment address.
123         if self.clause45 and self.opcode == 2 and self.clause45_addr != -1:
124             self.clause45_addr += 1
125
126     def reset_decoder_state(self):
127         self.mdiobits = []
128         self.bitcount = -1
129         self.opcode = -1
130         self.clause45 = 0
131         self.ss_frame = -1
132         self.ss_frame_field = -1
133         self.preamble_len = 0
134         self.ta_invalid = -1
135         self.op_invalid = ''
136         self.portad = -1
137         self.portad_bits = 5
138         self.devad = -1
139         self.devad_bits = 5
140         self.data = -1
141         self.data_bits = 16
142         self.state = 'PRE'
143
144     def state_PRE(self, mdio):
145         if self.illegal_bus:
146             if mdio == 0:   # Stay in illegal bus state.
147                 return
148             else:           # Leave and continue parsing.
149                 self.illegal_bus = 0
150                 self.put(self.ss_illegal, self.samplenum, self.out_ann,
151                          [4, ['ILLEGAL BUS STATE', 'ILL']])
152                 self.ss_frame = self.samplenum
153
154         if self.ss_frame == -1:
155             self.ss_frame = self.samplenum
156
157         if mdio == 1:
158             self.preamble_len += 1
159
160         # Valid MDIO can't clock more than 16 succeeding ones without being
161         # in either IDLE or PRE.
162         if self.preamble_len > 16:
163             if self.preamble_len >= 10000 + 32:
164                 self.put(self.ss_frame, self.mdiobits[32][1], self.out_ann,
165                     [3, ['IDLE #%d' % (self.preamble_len - 32), 'IDLE', 'I']])
166                 self.ss_frame = self.mdiobits[32][1]
167                 self.preamble_len = 32
168                 # This is getting out of hand, free some memory.
169                 del self.mdiobits[33:-1]
170             if mdio == 0:
171                 if self.preamble_len < 32:
172                     self.ss_frame = self.mdiobits[self.preamble_len][1]
173                     self.put(self.ss_frame, self.samplenum, self.out_ann,
174                              [4, ['SHORT PREAMBLE', 'SHRT PRE']])
175                 elif self.preamble_len > 32:
176                     self.ss_frame = self.mdiobits[32][1]
177                     self.put(self.mdiobits[self.preamble_len][1],
178                              self.mdiobits[32][1], self.out_ann,
179                              [3, ['IDLE #%d' % (self.preamble_len - 32),
180                              'IDLE', 'I']])
181                     self.preamble_len = 32
182                 else:
183                     self.ss_frame = self.mdiobits[32][1]
184                 self.put(self.ss_frame, self.samplenum, self.out_ann,
185                          [2, ['PRE #%d' % self.preamble_len, 'PRE', 'P']])
186                 self.ss_frame_field = self.samplenum
187                 self.state = 'ST'
188         elif mdio == 0:
189                 self.ss_illegal = self.ss_frame
190                 self.illegal_bus = 1
191
192     def state_ST(self, mdio):
193         if mdio == 0:
194             self.clause45 = 1
195         self.state = 'OP'
196
197     def state_OP(self, mdio):
198         if self.opcode == -1:
199             if self.clause45:
200                 st = ['ST (Clause 45)', 'ST 45']
201             else:
202                 st = ['ST (Clause 22)', 'ST 22']
203             self.putff([2, st + ['ST', 'S']])
204             self.ss_frame_field = self.samplenum
205
206             if mdio:
207                 self.opcode = 2
208             else:
209                 self.opcode = 0
210         else:
211             if self.clause45:
212                 self.state = 'PRTAD'
213                 self.opcode += mdio
214             else:
215                 if mdio == self.opcode:
216                     self.op_invalid = 'invalid for Clause 22'
217                 self.state = 'PRTAD'
218
219     def state_PRTAD(self, mdio):
220         if self.portad == -1:
221             self.portad = 0
222             if self.clause45:
223                 if self.opcode == 0:
224                     op = ['OP: ADDR', 'OP: A']
225                 elif self.opcode == 1:
226                     op = ['OP: WRITE', 'OP: W']
227                 elif self.opcode == 2:
228                     op = ['OP: READINC', 'OP: RI']
229                 elif self.opcode == 3:
230                     op = ['OP: READ', 'OP: R']
231             else:
232                 op = ['OP: READ', 'OP: R'] if self.opcode else ['OP: WRITE', 'OP: W']
233             self.putff([2, op + ['OP', 'O']])
234             if self.op_invalid:
235                 self.putff([4, ['OP %s' % self.op_invalid, 'OP', 'O']])
236             self.ss_frame_field = self.samplenum
237         self.portad_bits -= 1
238         self.portad |= mdio << self.portad_bits
239         if not self.portad_bits:
240             self.state = 'DEVAD'
241
242     def state_DEVAD(self, mdio):
243         if self.devad == -1:
244             self.devad = 0
245             if self.clause45:
246                 prtad = ['PRTAD: %02d' % self.portad, 'PRT', 'P']
247             else:
248                 prtad = ['PHYAD: %02d' % self.portad, 'PHY', 'P']
249             self.putff([2, prtad])
250             self.ss_frame_field = self.samplenum
251         self.devad_bits -= 1
252         self.devad |= mdio << self.devad_bits
253         if not self.devad_bits:
254             self.state = 'TA'
255
256     def state_TA(self, mdio):
257         if self.ta_invalid == -1:
258             self.ta_invalid = ''
259             if self.clause45:
260                 regad = ['DEVAD: %02d' % self.devad, 'DEV', 'D']
261             else:
262                 regad = ['REGAD: %02d' % self.devad, 'REG', 'R']
263             self.putff([2, regad])
264             self.ss_frame_field = self.samplenum
265             if mdio != 1 and ((self.clause45 and self.opcode < 2)
266             or (not self.clause45 and self.opcode == 0)):
267                 self.ta_invalid = ' invalid (bit1)'
268         else:
269             if mdio != 0:
270                 if self.ta_invalid:
271                     self.ta_invalid = ' invalid (bit1 and bit2)'
272                 else:
273                     self.ta_invalid = ' invalid (bit2)'
274             self.state = 'DATA'
275
276     def state_DATA(self, mdio):
277         if self.data == -1:
278             self.data = 0
279             self.putff([2, ['TA', 'T']])
280             if self.ta_invalid:
281                 self.putff([4, ['TA%s' % self.ta_invalid, 'TA', 'T']])
282             self.ss_frame_field = self.samplenum
283         self.data_bits -= 1
284         self.data |= mdio << self.data_bits
285         if not self.data_bits:
286             # Output final bit.
287             self.mdiobits[0][2] = self.mdiobits[0][1] + self.quartile_cycle_length()
288             self.bitcount += 1
289             self.putbit(self.mdiobits[0][0], self.mdiobits[0][1], self.mdiobits[0][2])
290             self.putdata()
291             self.reset_decoder_state()
292
293     def process_state(self, argument, mdio):
294         method_name = 'state_' + str(argument)
295         method = getattr(self, method_name)
296         return method(mdio)
297
298     # Returns the first quartile point of the frames cycle lengths. This is a
299     # conservative guess for the end of the last cycle. On average it will be
300     # more likely to fall short, than being too long, which makes for better
301     # readability in GUIs.
302     def quartile_cycle_length(self):
303         # 48 is the minimum number of samples we have to have at the end of a
304         # frame. The last sample only has a leading clock edge and is ignored.
305         bitlen = []
306         for i in range(1, 49):
307             bitlen.append(self.mdiobits[i][2] - self.mdiobits[i][1])
308         bitlen = sorted(bitlen)
309         return bitlen[12]
310
311     def handle_bit(self, mdio):
312         self.bitcount += 1
313         self.mdiobits.insert(0, [mdio, self.samplenum, -1])
314
315         if self.bitcount > 0:
316             self.mdiobits[1][2] = self.samplenum # Note end of last cycle.
317             # Output the last bit we processed.
318             self.putbit(self.mdiobits[1][0], self.mdiobits[1][1], self.mdiobits[1][2])
319
320         self.process_state(self.state, mdio)
321
322     def decode(self):
323         while True:
324             # Process pin state upon rising MDC edge.
325             pins = self.wait({0: 'r'})
326             self.handle_bit(pins[1])