]> sigrok.org Git - libsigrokdecode.git/blame - decoders/mdio/pd.py
All PDs: Consistently use singular/plural for annotation classes/rows.
[libsigrokdecode.git] / decoders / mdio / pd.py
CommitLineData
d12e106f
EO
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
29import sigrokdecode as srd
30
31class Decoder(srd.Decoder):
00177e99 32 api_version = 3
d12e106f
EO
33 id = 'mdio'
34 name = 'MDIO'
35 longname = 'Management Data Input/Output'
2787cf2a 36 desc = 'MII management bus between MAC and PHY.'
d12e106f
EO
37 license = 'bsd'
38 inputs = ['logic']
39 outputs = ['mdio']
d6d8a8a4 40 tags = ['Networking']
d12e106f
EO
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 = (
e144452b
UH
58 ('bit-vals', 'Bit values', (0,)),
59 ('bit-nums', 'Bit numbers', (1,)),
60 ('frames', 'Frames', (2, 3)),
61 ('frame-errors', 'Frame errors', (4,)),
62 ('decode-vals', 'Decode', (5,)),
d12e106f
EO
63 )
64
65 def __init__(self):
10aeb8ea
GS
66 self.reset()
67
68 def reset(self):
d12e106f 69 self.illegal_bus = 0
d12e106f
EO
70 self.clause45_addr = -1 # Clause 45 is context sensitive.
71 self.reset_decoder_state()
72
73 def start(self):
74 self.out_python = self.register(srd.OUTPUT_PYTHON)
75 self.out_ann = self.register(srd.OUTPUT_ANN)
76
77 def putbit(self, mdio, ss, es):
78 self.put(ss, es, self.out_ann, [0, ['%d' % mdio]])
79 if self.options['show_debug_bits'] == 'yes':
80 self.put(ss, es, self.out_ann, [1, ['%d' % (self.bitcount - 1), '%d' % ((self.bitcount - 1) % 10)]])
81
2452e2a3
UH
82 def putff(self, data):
83 self.put(self.ss_frame_field, self.samplenum, self.out_ann, data)
84
d12e106f
EO
85 def putdata(self):
86 self.put(self.ss_frame_field, self.mdiobits[0][2], self.out_ann,
87 [2, ['DATA: %04X' % self.data, 'DATA', 'D']])
88
89 if self.clause45 and self.opcode == 0:
90 self.clause45_addr = self.data
91
92 # Decode data.
93 if self.opcode > 0 or not self.clause45:
94 decoded_min = ''
95 if self.clause45 and self.clause45_addr != -1:
96 decoded_min += str.format('ADDR: %04X ' % self.clause45_addr)
97 elif self.clause45:
389d21d2 98 decoded_min += str.format('ADDR: UKWN ')
d12e106f
EO
99
100 if self.clause45 and self.opcode > 1 \
101 or (not self.clause45 and self.opcode):
102 decoded_min += str.format('READ: %04X' % self.data)
103 is_read = 1
104 else:
105 decoded_min += str.format('WRITE: %04X' % self.data)
106 is_read = 0
107 decoded_ext = str.format(' %s: %02d' % \
108 ('PRTAD' if self.clause45 else 'PHYAD', self.portad))
109 decoded_ext += str.format(' %s: %02d' % \
110 ('DEVAD' if self.clause45 else 'REGAD', self.devad))
111 if self.ta_invalid or self.op_invalid:
112 decoded_ext += ' ERROR'
113 self.put(self.ss_frame, self.mdiobits[0][2], self.out_ann,
114 [5, [decoded_min + decoded_ext, decoded_min]])
115
116 self.put(self.ss_frame, self.mdiobits[0][2], self.out_python,
117 [(bool(self.clause45), int(self.clause45_addr), \
118 bool(is_read), int(self.portad), int(self.devad), \
119 int(self.data))])
120
121 # Post read increment address.
122 if self.clause45 and self.opcode == 2 and self.clause45_addr != -1:
123 self.clause45_addr += 1
124
125 def reset_decoder_state(self):
126 self.mdiobits = []
127 self.bitcount = -1
128 self.opcode = -1
129 self.clause45 = 0
130 self.ss_frame = -1
131 self.ss_frame_field = -1
132 self.preamble_len = 0
133 self.ta_invalid = -1
134 self.op_invalid = ''
135 self.portad = -1
136 self.portad_bits = 5
137 self.devad = -1
138 self.devad_bits = 5
139 self.data = -1
140 self.data_bits = 16
141 self.state = 'PRE'
142
143 def state_PRE(self, mdio):
144 if self.illegal_bus:
145 if mdio == 0: # Stay in illegal bus state.
146 return
147 else: # Leave and continue parsing.
148 self.illegal_bus = 0
149 self.put(self.ss_illegal, self.samplenum, self.out_ann,
150 [4, ['ILLEGAL BUS STATE', 'ILL']])
151 self.ss_frame = self.samplenum
152
153 if self.ss_frame == -1:
154 self.ss_frame = self.samplenum
155
156 if mdio == 1:
157 self.preamble_len += 1
158
159 # Valid MDIO can't clock more than 16 succeeding ones without being
160 # in either IDLE or PRE.
161 if self.preamble_len > 16:
162 if self.preamble_len >= 10000 + 32:
163 self.put(self.ss_frame, self.mdiobits[32][1], self.out_ann,
164 [3, ['IDLE #%d' % (self.preamble_len - 32), 'IDLE', 'I']])
165 self.ss_frame = self.mdiobits[32][1]
166 self.preamble_len = 32
167 # This is getting out of hand, free some memory.
168 del self.mdiobits[33:-1]
169 if mdio == 0:
170 if self.preamble_len < 32:
171 self.ss_frame = self.mdiobits[self.preamble_len][1]
172 self.put(self.ss_frame, self.samplenum, self.out_ann,
173 [4, ['SHORT PREAMBLE', 'SHRT PRE']])
174 elif self.preamble_len > 32:
175 self.ss_frame = self.mdiobits[32][1]
176 self.put(self.mdiobits[self.preamble_len][1],
177 self.mdiobits[32][1], self.out_ann,
178 [3, ['IDLE #%d' % (self.preamble_len - 32),
179 'IDLE', 'I']])
180 self.preamble_len = 32
181 else:
182 self.ss_frame = self.mdiobits[32][1]
183 self.put(self.ss_frame, self.samplenum, self.out_ann,
184 [2, ['PRE #%d' % self.preamble_len, 'PRE', 'P']])
185 self.ss_frame_field = self.samplenum
186 self.state = 'ST'
187 elif mdio == 0:
188 self.ss_illegal = self.ss_frame
189 self.illegal_bus = 1
190
191 def state_ST(self, mdio):
192 if mdio == 0:
193 self.clause45 = 1
194 self.state = 'OP'
195
196 def state_OP(self, mdio):
197 if self.opcode == -1:
198 if self.clause45:
199 st = ['ST (Clause 45)', 'ST 45']
200 else:
201 st = ['ST (Clause 22)', 'ST 22']
2452e2a3 202 self.putff([2, st + ['ST', 'S']])
d12e106f
EO
203 self.ss_frame_field = self.samplenum
204
205 if mdio:
206 self.opcode = 2
207 else:
208 self.opcode = 0
209 else:
210 if self.clause45:
211 self.state = 'PRTAD'
212 self.opcode += mdio
213 else:
214 if mdio == self.opcode:
215 self.op_invalid = 'invalid for Clause 22'
216 self.state = 'PRTAD'
217
218 def state_PRTAD(self, mdio):
219 if self.portad == -1:
220 self.portad = 0
221 if self.clause45:
222 if self.opcode == 0:
223 op = ['OP: ADDR', 'OP: A']
224 elif self.opcode == 1:
225 op = ['OP: WRITE', 'OP: W']
226 elif self.opcode == 2:
227 op = ['OP: READINC', 'OP: RI']
228 elif self.opcode == 3:
229 op = ['OP: READ', 'OP: R']
230 else:
231 op = ['OP: READ', 'OP: R'] if self.opcode else ['OP: WRITE', 'OP: W']
2452e2a3 232 self.putff([2, op + ['OP', 'O']])
d12e106f 233 if self.op_invalid:
2452e2a3 234 self.putff([4, ['OP %s' % self.op_invalid, 'OP', 'O']])
d12e106f
EO
235 self.ss_frame_field = self.samplenum
236 self.portad_bits -= 1
237 self.portad |= mdio << self.portad_bits
238 if not self.portad_bits:
239 self.state = 'DEVAD'
240
241 def state_DEVAD(self, mdio):
242 if self.devad == -1:
243 self.devad = 0
244 if self.clause45:
245 prtad = ['PRTAD: %02d' % self.portad, 'PRT', 'P']
246 else:
247 prtad = ['PHYAD: %02d' % self.portad, 'PHY', 'P']
2452e2a3 248 self.putff([2, prtad])
d12e106f
EO
249 self.ss_frame_field = self.samplenum
250 self.devad_bits -= 1
251 self.devad |= mdio << self.devad_bits
252 if not self.devad_bits:
253 self.state = 'TA'
254
255 def state_TA(self, mdio):
256 if self.ta_invalid == -1:
257 self.ta_invalid = ''
258 if self.clause45:
259 regad = ['DEVAD: %02d' % self.devad, 'DEV', 'D']
260 else:
261 regad = ['REGAD: %02d' % self.devad, 'REG', 'R']
2452e2a3 262 self.putff([2, regad])
d12e106f
EO
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)'
267 else:
268 if mdio != 0:
269 if self.ta_invalid:
270 self.ta_invalid = ' invalid (bit1 and bit2)'
271 else:
272 self.ta_invalid = ' invalid (bit2)'
273 self.state = 'DATA'
274
275 def state_DATA(self, mdio):
276 if self.data == -1:
277 self.data = 0
bf11741f 278 self.putff([2, ['TA', 'T']])
d12e106f 279 if self.ta_invalid:
bf11741f 280 self.putff([4, ['TA%s' % self.ta_invalid, 'TA', 'T']])
d12e106f
EO
281 self.ss_frame_field = self.samplenum
282 self.data_bits -= 1
283 self.data |= mdio << self.data_bits
284 if not self.data_bits:
285 # Output final bit.
286 self.mdiobits[0][2] = self.mdiobits[0][1] + self.quartile_cycle_length()
287 self.bitcount += 1
288 self.putbit(self.mdiobits[0][0], self.mdiobits[0][1], self.mdiobits[0][2])
289 self.putdata()
290 self.reset_decoder_state()
291
292 def process_state(self, argument, mdio):
293 method_name = 'state_' + str(argument)
294 method = getattr(self, method_name)
295 return method(mdio)
296
297 # Returns the first quartile point of the frames cycle lengths. This is a
298 # conservative guess for the end of the last cycle. On average it will be
299 # more likely to fall short, than being too long, which makes for better
300 # readability in GUIs.
301 def quartile_cycle_length(self):
302 # 48 is the minimum number of samples we have to have at the end of a
303 # frame. The last sample only has a leading clock edge and is ignored.
304 bitlen = []
305 for i in range(1, 49):
306 bitlen.append(self.mdiobits[i][2] - self.mdiobits[i][1])
307 bitlen = sorted(bitlen)
308 return bitlen[12]
309
310 def handle_bit(self, mdio):
311 self.bitcount += 1
312 self.mdiobits.insert(0, [mdio, self.samplenum, -1])
313
314 if self.bitcount > 0:
315 self.mdiobits[1][2] = self.samplenum # Note end of last cycle.
316 # Output the last bit we processed.
317 self.putbit(self.mdiobits[1][0], self.mdiobits[1][1], self.mdiobits[1][2])
318
319 self.process_state(self.state, mdio)
320
00177e99
GS
321 def decode(self):
322 while True:
323 # Process pin state upon rising MDC edge.
324 pins = self.wait({0: 'r'})
d12e106f 325 self.handle_bit(pins[1])