]>
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' | |
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): | |
10aeb8ea GS |
65 | self.reset() |
66 | ||
67 | def reset(self): | |
d12e106f EO |
68 | self.illegal_bus = 0 |
69 | self.samplenum = -1 | |
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]) |