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