]>
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): | |
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 = ( | |
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): | |
10aeb8ea GS |
66 | self.reset() |
67 | ||
68 | def reset(self): | |
d12e106f EO |
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 | ||
2452e2a3 UH |
83 | def putff(self, data): |
84 | self.put(self.ss_frame_field, self.samplenum, self.out_ann, data) | |
85 | ||
d12e106f EO |
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: | |
389d21d2 | 99 | decoded_min += str.format('ADDR: UKWN ') |
d12e106f EO |
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'] | |
2452e2a3 | 203 | self.putff([2, st + ['ST', 'S']]) |
d12e106f EO |
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'] | |
2452e2a3 | 233 | self.putff([2, op + ['OP', 'O']]) |
d12e106f | 234 | if self.op_invalid: |
2452e2a3 | 235 | self.putff([4, ['OP %s' % self.op_invalid, 'OP', 'O']]) |
d12e106f EO |
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'] | |
2452e2a3 | 249 | self.putff([2, prtad]) |
d12e106f EO |
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'] | |
2452e2a3 | 263 | self.putff([2, regad]) |
d12e106f EO |
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 | |
bf11741f | 279 | self.putff([2, ['TA', 'T']]) |
d12e106f | 280 | if self.ta_invalid: |
bf11741f | 281 | self.putff([4, ['TA%s' % self.ta_invalid, 'TA', 'T']]) |
d12e106f EO |
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 | ||
00177e99 GS |
322 | def decode(self): |
323 | while True: | |
324 | # Process pin state upon rising MDC edge. | |
325 | pins = self.wait({0: 'r'}) | |
d12e106f | 326 | self.handle_bit(pins[1]) |