]> sigrok.org Git - libsigrokdecode.git/blob - decoders/mdio/pd.py
Add an MDIO/SMI decoder.
[libsigrokdecode.git] / decoders / mdio / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 ##
20
21 import sigrokdecode as srd
22
23 class Decoder(srd.Decoder):
24     api_version = 2
25     id = 'mdio'
26     name = 'MDIO'
27     longname = 'Management Data Input/Output'
28     desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
29     license = 'gplv2+'
30     inputs = ['logic']
31     outputs = ['mdio']
32     channels = (
33         {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
34         {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
35     )
36     annotations = (
37         ('mdio-data', 'MDIO data'),
38         ('mdio-bits', 'MDIO bits'),
39         ('errors', 'Human-readable errors'),
40     )
41     annotation_rows = (
42         ('mdio-data', 'MDIO data', (0,)),
43         ('mdio-bits', 'MDIO bits', (1,)),
44         ('other', 'Other', (2,)),
45     )
46
47     def __init__(self):
48         self.oldmdc = 0
49         self.ss_block = -1
50         self.samplenum = -1
51         self.oldpins = None
52         self.reset_decoder_state()
53
54     def start(self):
55         self.out_python = self.register(srd.OUTPUT_PYTHON)
56         self.out_ann = self.register(srd.OUTPUT_ANN)
57
58     def putw(self, data):
59         self.put(self.ss_block, self.samplenum, self.out_ann, data)
60
61     def putbit(self, mdio, start, stop):
62         # Bit annotations.
63         self.put(start, stop, self.out_ann, [1, ['%d' % mdio]])
64
65     def putdata(self):
66         # FIXME: Only pass data, no bits.
67         # Pass MDIO bits and then data to the next PD up the stack.
68         ss, es = self.mdiobits[-1][1], self.mdiobits[0][2]
69
70         # self.put(ss, es, self.out_python, ['BITS', self.mdiobits])
71         self.put(ss, es, self.out_python, ['DATA', self.mdiodata])
72
73         # Bit annotations.
74         for bit in self.mdiobits:
75             self.put(bit[1], bit[2], self.out_ann, [1, ['%d' % bit[0]]])
76
77         # Error annotation if an error happened.
78         if self.error:
79             self.put(self.ss_bit, self.es_error, self.out_ann, [2, [self.error]])
80             return
81
82         op = 'READ' if self.operation else 'WRITE'
83
84         # Dataword annotations.
85         if self.ss_preamble != -1:
86             self.put(self.ss_preamble, self.ss_start, self.out_ann, [0, ['PREAMBLE']])
87         self.put(self.ss_start, self.ss_operation, self.out_ann, [0, ['START']])
88         self.put(self.ss_operation, self.ss_phy, self.out_ann, [0, [op]])
89         self.put(self.ss_phy, self.ss_reg, self.out_ann, [0, ['PHY: %d' % self.phy]])
90         self.put(self.ss_reg, self.ss_turnaround, self.out_ann, [0, ['REG: %d' % self.reg]])
91         self.put(self.ss_turnaround, self.ss_data, self.out_ann, [0, ['TURNAROUND']])
92         self.put(self.ss_data, self.es_data, self.out_ann, [0, ['DATA: %04X' % self.data]])
93
94     def reset_decoder_state(self):
95         self.mdiodata = 0
96         self.mdiobits = []
97         self.bitcount = 0
98         self.ss_preamble = -1
99         self.ss_start = -1
100         self.ss_operation = -1
101         self.ss_phy = -1
102         self.ss_reg = -1
103         self.ss_turnaround = -1
104         self.ss_data = -1
105         self.phy = 0
106         self.phy_bits = 0
107         self.reg = 0
108         self.reg_bits = 0
109         self.data = 0
110         self.data_bits = 0
111         self.state = 'PREAMBLE'
112         self.error = None
113
114     def parse_preamble(self, mdio):
115         if self.ss_preamble == -1:
116             self.ss_preamble = self.samplenum
117         if mdio != 1:
118             self.error = 'Invalid preamble: could not find 32 consecutive bits set to 1'
119             self.state = 'ERROR'
120         elif self.bitcount == 31:
121             self.state = 'START'
122
123     def parse_start(self, mdio):
124         if self.ss_start == -1:
125             if mdio != 0:
126                 self.error = 'Invalid start bits: should be 01'
127                 self.state = 'ERROR'
128             else:
129                 self.ss_start = self.samplenum
130         else:
131             if mdio != 1:
132                 self.error = 'Invalid start bits: should be 01'
133                 self.state = 'ERROR'
134             else:
135                 self.state = 'OPERATION'
136
137     def parse_operation(self, mdio):
138         if self.ss_operation == -1:
139             self.ss_operation = self.samplenum
140             self.operation = mdio
141         else:
142             if mdio == self.operation:
143                 self.error = 'Invalid operation bits'
144                 self.state = 'ERROR'
145             else:
146                 self.state = 'PHY'
147
148     def parse_phy(self, mdio):
149         if self.ss_phy == -1:
150             self.ss_phy = self.samplenum
151         self.phy_bits += 1
152         self.phy |= mdio << (5 - self.phy_bits)
153         if self.phy_bits == 5:
154             self.state = 'REG'
155
156     def parse_reg(self, mdio):
157         if self.ss_reg == -1:
158             self.ss_reg = self.samplenum
159         self.reg_bits += 1
160         self.reg |= mdio << (5 - self.reg_bits)
161         if self.reg_bits == 5:
162             self.state = 'TURNAROUND'
163
164     def parse_turnaround(self, mdio):
165         if self.ss_turnaround == -1:
166             if self.operation == 0 and mdio != 1:
167                 self.error = 'Invalid turnaround bits'
168                 self.state = 'ERROR'
169             else:
170                 self.ss_turnaround = self.samplenum
171         else:
172             if mdio != 0:
173                 self.error = 'Invalid turnaround bits'
174                 self.state = 'ERROR'
175             else:
176                 self.state = 'DATA'
177
178     def parse_data(self, mdio):
179         if self.ss_data == -1:
180             self.ss_data = self.samplenum
181         self.data_bits += 1
182         self.data |= mdio << (16 - self.data_bits)
183         if self.data_bits == 16:
184             self.es_data = self.samplenum + int((self.samplenum - self.ss_data) / 15)
185             self.state = 'DONE'
186
187     def parse_error(self, mdio):
188         if self.bitcount == 63:
189             self.es_error = self.samplenum + int((self.samplenum - self.ss_bit) / 63)
190             self.state = 'DONE'
191
192     def handle_bit(self, mdio):
193         # If this is the first bit of a command, save its sample number.
194         if self.bitcount == 0:
195             self.ss_bit = self.samplenum
196             # No preamble?
197             if mdio == 0:
198                 self.state = 'START'
199
200         # Guesstimate the endsample for this bit (can be overridden below).
201         es = self.samplenum
202         if self.bitcount > 0:
203             es += self.samplenum - self.mdiobits[0][1]
204
205         self.mdiobits.insert(0, [mdio, self.samplenum, es])
206
207         if self.bitcount > 0:
208             self.bitsamples = (self.samplenum - self.ss_bit) / self.bitcount
209             self.mdiobits[1][2] = self.samplenum
210
211         if self.state == 'PREAMBLE':
212             self.parse_preamble(mdio)
213         elif self.state == 'START':
214             self.parse_start(mdio)
215         elif self.state == 'OPERATION':
216             self.parse_operation(mdio)
217         elif self.state == 'PHY':
218             self.parse_phy(mdio)
219         elif self.state == 'REG':
220             self.parse_reg(mdio)
221         elif self.state == 'TURNAROUND':
222             self.parse_turnaround(mdio)
223         elif self.state == 'DATA':
224             self.parse_data(mdio)
225         elif self.state == 'ERROR':
226             self.parse_error(mdio)
227
228         self.bitcount += 1
229         if self.state == 'DONE':
230             self.putdata()
231             self.reset_decoder_state()
232
233     def find_mdc_edge(self, mdc, mdio):
234         # Output the current error annotation if the clock stopped running
235         if self.state == 'ERROR' and self.samplenum - self.clocksample > (1.5 * self.bitsamples):
236             self.es_error = self.clocksample + int((self.clocksample - self.ss_bit) / self.bitcount)
237             self.putdata()
238             self.reset_decoder_state()
239
240         # Ignore sample if the clock pin hasn't changed.
241         if mdc == self.oldmdc:
242             return
243
244         self.oldmdc = mdc
245
246         if mdc == 0:   # Sample on rising clock edge.
247             return
248
249         # Found the correct clock edge, now get/handle the bit(s).
250         self.clocksample = self.samplenum
251         self.handle_bit(mdio)
252
253     def decode(self, ss, es, data):
254         for (self.samplenum, pins) in data:
255             # Ignore identical samples early on (for performance reasons).
256             if self.oldpins == pins:
257                 continue
258             self.oldpins, (mdc, mdio) = pins, pins
259
260             self.find_mdc_edge(mdc, mdio)