0bf4c613c139379e304a6d0a78d1509a9347a725
[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 from .lists import *
26
27 OPCODE_MASK = 0b11100000
28 REG_ADDR_MASK = 0b00011111
29
30 OPCODE_HANDLERS = {
31     0b00000000: '_process_rcr',
32     0b00100000: '_process_rbm',
33     0b01000000: '_process_wcr',
34     0b01100000: '_process_wbm',
35     0b10000000: '_process_bfs',
36     0b10100000: '_process_bfc',
37     0b11100000: '_process_src',
38 }
39
40 (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC, ANN_DATA,
41 ANN_REG_ADDR, ANN_WARNING) = range(10)
42
43 REG_ADDR_ECON1 = 0x1F
44 BIT_ECON1_BSEL0 = 0b00000001
45 BIT_ECON1_BSEL1 = 0b00000010
46
47 class Decoder(srd.Decoder):
48     api_version = 3
49     id = 'enc28j60'
50     name = 'ENC28J60'
51     longname = 'Microchip ENC28J60'
52     desc = 'Microchip ENC28J60 10Base-T Ethernet controller protocol.'
53     license = 'mit'
54     inputs = ['spi']
55     outputs = []
56     tags = ['Embedded/industrial', 'Networking']
57     annotations = (
58         ('rcr', 'Read Control Register'),
59         ('rbm', 'Read Buffer Memory'),
60         ('wcr', 'Write Control Register'),
61         ('wbm', 'Write Buffer Memory'),
62         ('bfs', 'Bit Field Set'),
63         ('bfc', 'Bit Field Clear'),
64         ('src', 'System Reset Command'),
65         ('data', 'Data'),
66         ('reg-addr', 'Register Address'),
67         ('warning', 'Warning'),
68     )
69     annotation_rows = (
70         ('commands', 'Commands',
71             (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
72         ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
73         ('warnings', 'Warnings', (ANN_WARNING,)),
74     )
75
76     def __init__(self):
77         self.reset()
78
79     def reset(self):
80         self.mosi = []
81         self.miso = []
82         self.ranges = []
83         self.cmd_ss = None
84         self.cmd_es = None
85         self.active = False
86         self.bsel0 = None
87         self.bsel1 = None
88
89     def start(self):
90         self.ann = self.register(srd.OUTPUT_ANN)
91
92     def _process_command(self):
93         if len(self.mosi) == 0:
94             self.active = False
95             return
96
97         header = self.mosi[0]
98         opcode = header & OPCODE_MASK
99
100         if opcode not in OPCODE_HANDLERS:
101             self._put_command_warning("Unknown opcode.")
102             self.active = False
103             return
104
105         getattr(self, OPCODE_HANDLERS[opcode])()
106
107         self.active = False
108
109     def _get_register_name(self, reg_addr):
110         if (self.bsel0 is None) or (self.bsel1 is None):
111             # We don't know the bank we're in yet.
112             return None
113         else:
114             bank = (self.bsel1 << 1) + self.bsel0
115             return REGS[bank][reg_addr]
116
117     def _put_register_header(self):
118         reg_addr = self.mosi[0] & REG_ADDR_MASK
119         reg_name = self._get_register_name(reg_addr)
120
121         ss, es = self.cmd_ss, self.ranges[1][0]
122
123         if reg_name is None:
124             # We don't know the bank we're in yet.
125             self.put(ss, es, self.ann, [
126                      ANN_REG_ADDR,
127                      [
128                         'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
129                         '?:{0:02X}'.format(reg_addr),
130                      ]])
131             self.put(ss, es, self.ann, [
132                      ANN_WARNING,
133                      [
134                         'Warning: Register bank not known yet.',
135                         'Warning',
136                      ]])
137         else:
138             self.put(ss, es, self.ann, [
139                      ANN_REG_ADDR,
140                      [
141                         'Reg {0}'.format(reg_name),
142                         '{0}'.format(reg_name),
143                      ]])
144
145             if (reg_name == '-') or (reg_name == 'Reserved'):
146                 self.put(ss, es, self.ann, [
147                          ANN_WARNING,
148                          [
149                             'Warning: Invalid register accessed.',
150                             'Warning',
151                          ]])
152
153     def _put_data_byte(self, data, byte_index, binary=False):
154         ss = self.ranges[byte_index][0]
155         if byte_index == len(self.mosi) - 1:
156             es = self.cmd_es
157         else:
158             es = self.ranges[byte_index + 1][0]
159
160         if binary:
161             self.put(ss, es, self.ann, [
162                      ANN_DATA,
163                      [
164                         'Data 0b{0:08b}'.format(data),
165                         '{0:08b}'.format(data),
166                      ]])
167         else:
168             self.put(ss, es, self.ann, [
169                      ANN_DATA,
170                      [
171                         'Data 0x{0:02X}'.format(data),
172                         '{0:02X}'.format(data),
173                      ]])
174
175     def _put_command_warning(self, reason):
176         self.put(self.cmd_ss, self.cmd_es, self.ann, [
177                  ANN_WARNING,
178                  [
179                     'Warning: {0}'.format(reason),
180                     'Warning',
181                  ]])
182
183     def _process_rcr(self):
184         self.put(self.cmd_ss, self.cmd_es,
185                  self.ann, [ANN_RCR, ['Read Control Register', 'RCR']])
186
187         if (len(self.mosi) != 2) and (len(self.mosi) != 3):
188             self._put_command_warning('Invalid command length.')
189             return
190
191         self._put_register_header()
192
193         reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
194         if reg_name is None:
195             # We can't tell if we're accessing MAC/MII registers or not
196             # Let's trust the user in this case.
197             pass
198         else:
199             if (reg_name[0] == 'M') and (len(self.mosi) != 3):
200                 self._put_command_warning('Attempting to read a MAC/MII '
201                     + 'register without using the dummy byte.')
202                 return
203
204             if (reg_name[0] != 'M') and (len(self.mosi) != 2):
205                 self._put_command_warning('Attempting to read a non-MAC/MII '
206                                           + 'register using the dummy byte.')
207                 return
208
209         if len(self.mosi) == 2:
210             self._put_data_byte(self.miso[1], 1)
211         else:
212             ss, es = self.ranges[1][0], self.ranges[2][0]
213             self.put(ss, es, self.ann, [
214                      ANN_DATA,
215                      [
216                         'Dummy Byte',
217                         'Dummy',
218                      ]])
219             self._put_data_byte(self.miso[2], 2)
220
221     def _process_rbm(self):
222         if self.mosi[0] != 0b00111010:
223             self._put_command_warning('Invalid header byte.')
224             return
225
226         self.put(self.cmd_ss, self.cmd_es, self.ann, [
227                  ANN_RBM,
228                  [
229                     'Read Buffer Memory: Length {0}'.format(
230                         len(self.mosi) - 1),
231                     'RBM',
232                  ]])
233
234         for i in range(1, len(self.miso)):
235             self._put_data_byte(self.miso[i], i)
236
237     def _process_wcr(self):
238         self.put(self.cmd_ss, self.cmd_es,
239                  self.ann, [ANN_WCR, ['Write Control Register', 'WCR']])
240
241         if len(self.mosi) != 2:
242             self._put_command_warning('Invalid command length.')
243             return
244
245         self._put_register_header()
246         self._put_data_byte(self.mosi[1], 1)
247
248         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
249             self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
250             self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
251
252     def _process_wbm(self):
253         if self.mosi[0] != 0b01111010:
254             self._put_command_warning('Invalid header byte.')
255             return
256
257         self.put(self.cmd_ss, self.cmd_es, self.ann, [
258                  ANN_WBM,
259                  [
260                     'Write Buffer Memory: Length {0}'.format(
261                         len(self.mosi) - 1),
262                     'WBM',
263                  ]])
264
265         for i in range(1, len(self.mosi)):
266             self._put_data_byte(self.mosi[i], i)
267
268     def _process_bfc(self):
269         self.put(self.cmd_ss, self.cmd_es,
270                  self.ann, [ANN_BFC, ['Bit Field Clear', 'BFC']])
271
272         if len(self.mosi) != 2:
273             self._put_command_warning('Invalid command length.')
274             return
275
276         self._put_register_header()
277         self._put_data_byte(self.mosi[1], 1, True)
278
279         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
280             if self.mosi[1] & BIT_ECON1_BSEL0:
281                 self.bsel0 = 0
282             if self.mosi[1] & BIT_ECON1_BSEL1:
283                 self.bsel1 = 0
284
285     def _process_bfs(self):
286         self.put(self.cmd_ss, self.cmd_es,
287                  self.ann, [ANN_BFS, ['Bit Field Set', 'BFS']])
288
289         if len(self.mosi) != 2:
290             self._put_command_warning('Invalid command length.')
291             return
292
293         self._put_register_header()
294         self._put_data_byte(self.mosi[1], 1, True)
295
296         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
297             if self.mosi[1] & BIT_ECON1_BSEL0:
298                 self.bsel0 = 1
299             if self.mosi[1] & BIT_ECON1_BSEL1:
300                 self.bsel1 = 1
301
302     def _process_src(self):
303         self.put(self.cmd_ss, self.cmd_es,
304                  self.ann, [ANN_SRC, ['System Reset Command', 'SRC']])
305
306         if len(self.mosi) != 1:
307             self._put_command_warning('Invalid command length.')
308             return
309
310         self.bsel0 = 0
311         self.bsel1 = 0
312
313     def decode(self, ss, es, data):
314         ptype, data1, data2 = data
315
316         if ptype == 'CS-CHANGE':
317             new_cs = data2
318
319             if new_cs == 0:
320                 self.active = True
321                 self.cmd_ss = ss
322                 self.mosi = []
323                 self.miso = []
324                 self.ranges = []
325             elif new_cs == 1:
326                 if self.active:
327                     self.cmd_es = es
328                     self._process_command()
329         elif ptype == 'DATA':
330             mosi, miso = data1, data2
331
332             self.mosi.append(mosi)
333             self.miso.append(miso)
334             self.ranges.append((ss, es))