]> sigrok.org Git - libsigrokdecode.git/blob - decoders/enc28j60/pd.py
enc28j60: Implements decoder for ENC28J60 SPI Ethernet chip
[libsigrokdecode.git] / decoders / enc28j60 / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
5 ##
6 ## Permission is hereby granted, free of charge, to any person obtaining a copy
7 ## of this software and associated documentation files (the "Software"), to deal
8 ## in the Software without restriction, including without limitation the rights
9 ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 ## copies of the Software, and to permit persons to whom the Software is
11 ## furnished to do so, subject to the following conditions:
12 ##
13 ## The above copyright notice and this permission notice shall be included in all
14 ## copies or substantial portions of the Software.
15 ##
16 ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 ## SOFTWARE.
23
24 import sigrokdecode as srd
25
26 OPCODE_MASK = 0b11100000
27 REG_ADDR_MASK = 0b00011111
28
29 OPCODE_HANDLERS = {
30     0b00000000: '_process_rcr',
31     0b00100000: '_process_rbm',
32     0b01000000: '_process_wcr',
33     0b01100000: '_process_wbm',
34     0b10000000: '_process_bfs',
35     0b10100000: '_process_bfc',
36     0b11100000: '_process_src',
37 }
38
39 ANN_RCR = 0
40 ANN_RBM = 1
41 ANN_WCR = 2
42 ANN_WBM = 3
43 ANN_BFS = 4
44 ANN_BFC = 5
45 ANN_SRC = 6
46
47 ANN_DATA = 7
48 ANN_REG_ADDR = 8
49
50 ANN_WARNING = 9
51
52 REG_ADDR_ECON1 = 0x1F
53 BIT_ECON1_BSEL0 = 0b00000001
54 BIT_ECON1_BSEL1 = 0b00000010
55
56 REGS = [
57     [
58         'ERDPTL',
59         'ERDPTH',
60         'EWRPTL',
61         'EWRPTH',
62         'ETXSTL',
63         'ETXSTH',
64         'ETXNDL',
65         'ETXNDH',
66         'ERXSTL',
67         'ERXSTH',
68         'ERXNDL',
69         'ERXNDH',
70         'ERXRDPTL',
71         'ERXRDPTH',
72         'ERXWRPTL',
73         'ERXWRPTH',
74         'EDMASTL',
75         'EDMASTH',
76         'EDMANDL',
77         'EDMANDH',
78         'EDMADSTL',
79         'EDMADSTH',
80         'EDMACSL',
81         'EDMACSH',
82         '—',
83         '—',
84         'Reserved',
85         'EIE',
86         'EIR',
87         'ESTAT',
88         'ECON2',
89         'ECON1',
90     ],
91     [
92         'EHT0',
93         'EHT1',
94         'EHT2',
95         'EHT3',
96         'EHT4',
97         'EHT5',
98         'EHT6',
99         'EHT7',
100         'EPMM0',
101         'EPMM1',
102         'EPMM2',
103         'EPMM3',
104         'EPMM4',
105         'EPMM5',
106         'EPMM6',
107         'EPMM7',
108         'EPMCSL',
109         'EPMCSH',
110         '—',
111         '—',
112         'EPMOL',
113         'EPMOH',
114         'Reserved',
115         'Reserved',
116         'ERXFCON',
117         'EPKTCNT',
118         'Reserved',
119         'EIE',
120         'EIR',
121         'ESTAT',
122         'ECON2',
123         'ECON1',
124     ],
125     [
126         'MACON1',
127         'Reserved',
128         'MACON3',
129         'MACON4',
130         'MABBIPG',
131         '—',
132         'MAIPGL',
133         'MAIPGH',
134         'MACLCON1',
135         'MACLCON2',
136         'MAMXFLL',
137         'MAMXFLH',
138         'Reserved',
139         'Reserved',
140         'Reserved',
141         '—',
142         'Reserved',
143         'Reserved',
144         'MICMD',
145         '—',
146         'MIREGADR',
147         'Reserved',
148         'MIWRL',
149         'MIWRH',
150         'MIRDL',
151         'MIRDH',
152         'Reserved',
153         'EIE',
154         'EIR',
155         'ESTAT',
156         'ECON2',
157         'ECON1',
158     ],
159     [
160         'MAADR5',
161         'MAADR6',
162         'MAADR3',
163         'MAADR4',
164         'MAADR1',
165         'MAADR2',
166         'EBSTSD',
167         'EBSTCON',
168         'EBSTCSL',
169         'EBSTCSH',
170         'MISTAT',
171         '—',
172         '—',
173         '—',
174         '—',
175         '—',
176         '—',
177         '—',
178         'EREVID',
179         '—',
180         '—',
181         'ECOCON',
182         'Reserved',
183         'EFLOCON',
184         'EPAUSL',
185         'EPAUSH',
186         'Reserved',
187         'EIE',
188         'EIR',
189         'ESTAT',
190         'ECON2',
191         'ECON1',
192     ],
193 ]
194
195 class Decoder(srd.Decoder):
196     api_version = 3
197     id = 'enc28j60'
198     name = 'ENC28J60'
199     longname = 'Microchip ENC28J60'
200     desc = 'Microchip ENC28J60 10Base-T Ethernet controller protocol.'
201     license = 'mit'
202     inputs = ['spi']
203     outputs = ['enc28j60']
204     annotations = (
205         ('rcr', 'Read Control Register'),
206         ('rbm', 'Read Buffer Memory'),
207         ('wcr', 'Write Control Register'),
208         ('wbm', 'Write Buffer Memory'),
209         ('bfs', 'Bit Field Set'),
210         ('bfc', 'Bit Field Clear'),
211         ('src', 'System Reset Command'),
212         ('data', 'Data'),
213         ('reg-addr', 'Register Address'),
214         ('warning', 'Warning'),
215     )
216     annotation_rows = (
217         ('commands', 'Commands',
218             (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
219         ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
220         ('warnings', 'Warnings', (ANN_WARNING,)),
221     )
222
223     def __init__(self):
224         self.reset()
225
226     def reset(self):
227         self.mosi = []
228         self.miso = []
229         self.ranges = []
230         self.command_start = None
231         self.command_end = None
232         self.active = False
233         self.bsel0 = None
234         self.bsel1 = None
235
236     def start(self):
237         self.ann = self.register(srd.OUTPUT_ANN)
238
239     def _process_command(self):
240         if len(self.mosi) == 0:
241             self.active = False
242             return
243
244         header = self.mosi[0]
245         opcode = header & OPCODE_MASK
246
247         if opcode not in OPCODE_HANDLERS:
248             self._put_command_warning("Unknown opcode.")
249             self.active = False
250             return
251
252         getattr(self, OPCODE_HANDLERS[opcode])()
253
254         self.active = False
255
256     def _get_register_name(self, reg_addr):
257         if (self.bsel0 is None) or (self.bsel1 is None):
258             # We don't know the bank we're in yet.
259             return None
260         else:
261             bank = (self.bsel1 << 1) + self.bsel0
262             return REGS[bank][reg_addr]
263
264     def _put_register_header(self):
265         reg_addr = self.mosi[0] & REG_ADDR_MASK
266         reg_name = self._get_register_name(reg_addr)
267
268         if reg_name is None:
269             # We don't know the bank we're in yet.
270             self.put(self.command_start, self.ranges[1][0], self.ann, [
271                      ANN_REG_ADDR,
272                      [
273                         'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
274                         '?:{0:02X}'.format(reg_addr),
275                      ]])
276             self.put(self.command_start, self.ranges[1][0], self.ann, [
277                      ANN_WARNING,
278                      [
279                         'Warning: Register bank not known yet.',
280                         'Warning',
281                      ]])
282         else:
283             self.put(self.command_start, self.ranges[1][0], self.ann, [
284                      ANN_REG_ADDR,
285                      [
286                         'Reg {0}'.format(reg_name),
287                         '{0}'.format(reg_name),
288                      ]])
289
290             if (reg_name == '-') or (reg_name == 'Reserved'):
291                 self.put(self.command_start, self.ranges[1][0], self.ann, [
292                          ANN_WARNING,
293                          [
294                             'Warning: Invalid register accessed.',
295                             'Warning',
296                          ]])
297
298     def _put_data_byte(self, data, byte_index, binary=False):
299         if byte_index == len(self.mosi) - 1:
300             end_sample = self.command_end
301         else:
302             end_sample = self.ranges[byte_index + 1][0]
303
304         if binary:
305             self.put(self.ranges[byte_index][0], end_sample, self.ann, [
306                      ANN_DATA,
307                      [
308                         'Data 0b{0:08b}'.format(data),
309                         '{0:08b}'.format(data),
310                      ]])
311         else:
312             self.put(self.ranges[byte_index][0], end_sample, self.ann, [
313                      ANN_DATA,
314                      [
315                         'Data 0x{0:02X}'.format(data),
316                         '{0:02X}'.format(data),
317                      ]])
318
319     def _put_command_warning(self, reason):
320         self.put(self.command_start, self.command_end, self.ann, [
321                  ANN_WARNING,
322                  [
323                     'Warning: {0}'.format(reason),
324                     'Warning',
325                  ]])
326
327     def _process_rcr(self):
328         self.put(self.command_start, self.command_end,
329                  self.ann, [ANN_RCR, ['Read Control Register', 'RCR']])
330
331         if (len(self.mosi) != 2) and (len(self.mosi) != 3):
332             self._put_command_warning('Invalid command length.')
333             return
334
335         self._put_register_header()
336
337         reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
338         if reg_name is None:
339             # We can't tell if we're accessing MAC/MII registers or not
340             # Let's trust the user in this case.
341             pass
342         else:
343             if (reg_name[0] == 'M') and (len(self.mosi) != 3):
344                 self._put_command_warning('Attempting to read a MAC/MII '
345                     + 'register without using the dummy byte.')
346                 return
347
348             if (reg_name[0] != 'M') and (len(self.mosi) != 2):
349                 self._put_command_warning('Attempting to read a non-MAC/MII '
350                                           + 'register using the dummy byte.')
351                 return
352
353         if len(self.mosi) == 2:
354             self._put_data_byte(self.miso[1], 1)
355         else:
356             self.put(self.ranges[1][0], self.ranges[2][0], self.ann, [
357                      ANN_DATA,
358                      [
359                         'Dummy Byte',
360                         'Dummy',
361                      ]])
362             self._put_data_byte(self.miso[2], 2)
363
364     def _process_rbm(self):
365         if self.mosi[0] != 0b00111010:
366             self._put_command_warning('Invalid header byte.')
367             return
368
369         self.put(self.command_start, self.command_end, self.ann, [
370                  ANN_RBM,
371                  [
372                     'Read Buffer Memory: Length {0}'.format(
373                         len(self.mosi) - 1),
374                     'RBM',
375                  ]])
376
377         for i in range(1, len(self.miso)):
378             self._put_data_byte(self.miso[i], i)
379
380     def _process_wcr(self):
381         self.put(self.command_start, self.command_end,
382                  self.ann, [ANN_WCR, ['Write Control Register', 'WCR']])
383
384         if len(self.mosi) != 2:
385             self._put_command_warning('Invalid command length.')
386             return
387
388         self._put_register_header()
389         self._put_data_byte(self.mosi[1], 1)
390
391         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
392             self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
393             self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
394
395     def _process_wbm(self):
396         if self.mosi[0] != 0b01111010:
397             self._put_command_warning('Invalid header byte.')
398             return
399
400         self.put(self.command_start, self.command_end, self.ann, [
401                  ANN_WBM,
402                  [
403                     'Write Buffer Memory: Length {0}'.format(
404                         len(self.mosi) - 1),
405                     'WBM',
406                  ]])
407
408         for i in range(1, len(self.mosi)):
409             self._put_data_byte(self.mosi[i], i)
410
411     def _process_bfc(self):
412         self.put(self.command_start, self.command_end,
413                  self.ann, [ANN_BFC, ['Bit Field Clear', 'BFC']])
414
415         if len(self.mosi) != 2:
416             self._put_command_warning('Invalid command length.')
417             return
418
419         self._put_register_header()
420         self._put_data_byte(self.mosi[1], 1, True)
421
422         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
423             if self.mosi[1] & BIT_ECON1_BSEL0:
424                 self.bsel0 = 0
425             if self.mosi[1] & BIT_ECON1_BSEL1:
426                 self.bsel1 = 0
427
428     def _process_bfs(self):
429         self.put(self.command_start, self.command_end,
430                  self.ann, [ANN_BFS, ['Bit Field Set', 'BFS']])
431
432         if len(self.mosi) != 2:
433             self._put_command_warning('Invalid command length.')
434             return
435
436         self._put_register_header()
437         self._put_data_byte(self.mosi[1], 1, True)
438
439         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
440             if self.mosi[1] & BIT_ECON1_BSEL0:
441                 self.bsel0 = 1
442             if self.mosi[1] & BIT_ECON1_BSEL1:
443                 self.bsel1 = 1
444
445     def _process_src(self):
446         self.put(self.command_start, self.command_end,
447                  self.ann, [ANN_SRC, ['System Reset Command', 'SRC']])
448
449         if len(self.mosi) != 1:
450             self._put_command_warning('Invalid command length.')
451             return
452
453         self.bsel0 = 0
454         self.bsel1 = 0
455
456     def decode(self, ss, es, data):
457         ptype, data1, data2 = data
458
459         if ptype == 'CS-CHANGE':
460             new_cs = data2
461
462             if new_cs == 0:
463                 self.active = True
464                 self.command_start = ss
465                 self.mosi = []
466                 self.miso = []
467                 self.ranges = []
468             elif new_cs == 1:
469                 if self.active:
470                     self.command_end = es
471                     self._process_command()
472         elif ptype == 'DATA':
473             mosi, miso = data1, data2
474
475             self.mosi.append(mosi)
476             self.miso.append(miso)
477             self.ranges.append((ss, es))