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