2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
5 ## All rights reserved.
7 ## Redistribution and use in source and binary forms, with or without
8 ## modification, are permitted provided that the following conditions are met:
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.
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.
29 import sigrokdecode as srd
31 class Decoder(srd.Decoder):
35 longname = 'Management Data Input/Output'
36 desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
41 {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
42 {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
45 {'id': 'show_debug_bits', 'desc': 'Show debug bits',
46 'default': 'no', 'values': ('yes', 'no')},
49 ('bit-val', 'Bit value'),
50 ('bit-num', 'Bit number'),
52 ('frame-idle', 'Bus idle state'),
53 ('frame-error', 'Frame error'),
57 ('bit-val', 'Bit value', (0,)),
58 ('bit-num', 'Bit number', (1,)),
59 ('frame', 'Frame', (2, 3)),
60 ('frame-error', 'Frame error', (4,)),
61 ('decode', 'Decode', (5,)),
68 self.clause45_addr = -1 # Clause 45 is context sensitive.
69 self.reset_decoder_state()
72 self.out_python = self.register(srd.OUTPUT_PYTHON)
73 self.out_ann = self.register(srd.OUTPUT_ANN)
75 def putbit(self, mdio, ss, es):
76 self.put(ss, es, self.out_ann, [0, ['%d' % mdio]])
77 if self.options['show_debug_bits'] == 'yes':
78 self.put(ss, es, self.out_ann, [1, ['%d' % (self.bitcount - 1), '%d' % ((self.bitcount - 1) % 10)]])
81 self.put(self.ss_frame_field, self.mdiobits[0][2], self.out_ann,
82 [2, ['DATA: %04X' % self.data, 'DATA', 'D']])
84 if self.clause45 and self.opcode == 0:
85 self.clause45_addr = self.data
88 if self.opcode > 0 or not self.clause45:
90 if self.clause45 and self.clause45_addr != -1:
91 decoded_min += str.format('ADDR: %04X ' % self.clause45_addr)
93 decoded_min += str.format('ADDR: UKWN ' % self.clause45_addr)
95 if self.clause45 and self.opcode > 1 \
96 or (not self.clause45 and self.opcode):
97 decoded_min += str.format('READ: %04X' % self.data)
100 decoded_min += str.format('WRITE: %04X' % self.data)
102 decoded_ext = str.format(' %s: %02d' % \
103 ('PRTAD' if self.clause45 else 'PHYAD', self.portad))
104 decoded_ext += str.format(' %s: %02d' % \
105 ('DEVAD' if self.clause45 else 'REGAD', self.devad))
106 if self.ta_invalid or self.op_invalid:
107 decoded_ext += ' ERROR'
108 self.put(self.ss_frame, self.mdiobits[0][2], self.out_ann,
109 [5, [decoded_min + decoded_ext, decoded_min]])
111 self.put(self.ss_frame, self.mdiobits[0][2], self.out_python,
112 [(bool(self.clause45), int(self.clause45_addr), \
113 bool(is_read), int(self.portad), int(self.devad), \
116 # Post read increment address.
117 if self.clause45 and self.opcode == 2 and self.clause45_addr != -1:
118 self.clause45_addr += 1
120 def reset_decoder_state(self):
126 self.ss_frame_field = -1
127 self.preamble_len = 0
138 def state_PRE(self, mdio):
140 if mdio == 0: # Stay in illegal bus state.
142 else: # Leave and continue parsing.
144 self.put(self.ss_illegal, self.samplenum, self.out_ann,
145 [4, ['ILLEGAL BUS STATE', 'ILL']])
146 self.ss_frame = self.samplenum
148 if self.ss_frame == -1:
149 self.ss_frame = self.samplenum
152 self.preamble_len += 1
154 # Valid MDIO can't clock more than 16 succeeding ones without being
155 # in either IDLE or PRE.
156 if self.preamble_len > 16:
157 if self.preamble_len >= 10000 + 32:
158 self.put(self.ss_frame, self.mdiobits[32][1], self.out_ann,
159 [3, ['IDLE #%d' % (self.preamble_len - 32), 'IDLE', 'I']])
160 self.ss_frame = self.mdiobits[32][1]
161 self.preamble_len = 32
162 # This is getting out of hand, free some memory.
163 del self.mdiobits[33:-1]
165 if self.preamble_len < 32:
166 self.ss_frame = self.mdiobits[self.preamble_len][1]
167 self.put(self.ss_frame, self.samplenum, self.out_ann,
168 [4, ['SHORT PREAMBLE', 'SHRT PRE']])
169 elif self.preamble_len > 32:
170 self.ss_frame = self.mdiobits[32][1]
171 self.put(self.mdiobits[self.preamble_len][1],
172 self.mdiobits[32][1], self.out_ann,
173 [3, ['IDLE #%d' % (self.preamble_len - 32),
175 self.preamble_len = 32
177 self.ss_frame = self.mdiobits[32][1]
178 self.put(self.ss_frame, self.samplenum, self.out_ann,
179 [2, ['PRE #%d' % self.preamble_len, 'PRE', 'P']])
180 self.ss_frame_field = self.samplenum
183 self.ss_illegal = self.ss_frame
186 def state_ST(self, mdio):
191 def state_OP(self, mdio):
192 if self.opcode == -1:
194 st = ['ST (Clause 45)', 'ST 45']
196 st = ['ST (Clause 22)', 'ST 22']
197 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
198 [2, st + ['ST', 'S']])
199 self.ss_frame_field = self.samplenum
210 if mdio == self.opcode:
211 self.op_invalid = 'invalid for Clause 22'
214 def state_PRTAD(self, mdio):
215 if self.portad == -1:
219 op = ['OP: ADDR', 'OP: A']
220 elif self.opcode == 1:
221 op = ['OP: WRITE', 'OP: W']
222 elif self.opcode == 2:
223 op = ['OP: READINC', 'OP: RI']
224 elif self.opcode == 3:
225 op = ['OP: READ', 'OP: R']
227 op = ['OP: READ', 'OP: R'] if self.opcode else ['OP: WRITE', 'OP: W']
228 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
229 [2, op + ['OP', 'O']])
231 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
232 [4, ['OP %s' % self.op_invalid, 'OP', 'O']])
233 self.ss_frame_field = self.samplenum
234 self.portad_bits -= 1
235 self.portad |= mdio << self.portad_bits
236 if not self.portad_bits:
239 def state_DEVAD(self, mdio):
243 prtad = ['PRTAD: %02d' % self.portad, 'PRT', 'P']
245 prtad = ['PHYAD: %02d' % self.portad, 'PHY', 'P']
246 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
248 self.ss_frame_field = self.samplenum
250 self.devad |= mdio << self.devad_bits
251 if not self.devad_bits:
254 def state_TA(self, mdio):
255 if self.ta_invalid == -1:
258 regad = ['DEVAD: %02d' % self.devad, 'DEV', 'D']
260 regad = ['REGAD: %02d' % self.devad, 'REG', 'R']
261 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
263 self.ss_frame_field = self.samplenum
264 if mdio != 1 and ((self.clause45 and self.opcode < 2)
265 or (not self.clause45 and self.opcode == 0)):
266 self.ta_invalid = ' invalid (bit1)'
270 self.ta_invalid = ' invalid (bit1 and bit2)'
272 self.ta_invalid = ' invalid (bit2)'
275 def state_DATA(self, mdio):
278 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
281 self.put(self.ss_frame_field, self.samplenum, self.out_ann,
282 [4, ['TA%s' % self.ta_invalid, 'TA', 'T']])
283 self.ss_frame_field = self.samplenum
285 self.data |= mdio << self.data_bits
286 if not self.data_bits:
288 self.mdiobits[0][2] = self.mdiobits[0][1] + self.quartile_cycle_length()
290 self.putbit(self.mdiobits[0][0], self.mdiobits[0][1], self.mdiobits[0][2])
292 self.reset_decoder_state()
294 def process_state(self, argument, mdio):
295 method_name = 'state_' + str(argument)
296 method = getattr(self, method_name)
299 # Returns the first quartile point of the frames cycle lengths. This is a
300 # conservative guess for the end of the last cycle. On average it will be
301 # more likely to fall short, than being too long, which makes for better
302 # readability in GUIs.
303 def quartile_cycle_length(self):
304 # 48 is the minimum number of samples we have to have at the end of a
305 # frame. The last sample only has a leading clock edge and is ignored.
307 for i in range(1, 49):
308 bitlen.append(self.mdiobits[i][2] - self.mdiobits[i][1])
309 bitlen = sorted(bitlen)
312 def handle_bit(self, mdio):
314 self.mdiobits.insert(0, [mdio, self.samplenum, -1])
316 if self.bitcount > 0:
317 self.mdiobits[1][2] = self.samplenum # Note end of last cycle.
318 # Output the last bit we processed.
319 self.putbit(self.mdiobits[1][0], self.mdiobits[1][1], self.mdiobits[1][2])
321 self.process_state(self.state, mdio)
323 def decode(self, ss, es, data):
324 for (self.samplenum, pins) in data:
325 # Ignore identical samples early on (for performance reasons).
326 if self.last_mdc == pins[0]:
328 self.last_mdc = pins[0]
329 if pins[0] == 0: # Check for rising edge.
332 # Found the correct clock edge, now get/handle the bit(s).
333 self.handle_bit(pins[1])