enc28j60: Factor out self.putc().
[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 putc(self, data):
93         self.put(self.cmd_ss, self.cmd_es, self.ann, data)
94
95     def _process_command(self):
96         if len(self.mosi) == 0:
97             self.active = False
98             return
99
100         header = self.mosi[0]
101         opcode = header & OPCODE_MASK
102
103         if opcode not in OPCODE_HANDLERS:
104             self._put_command_warning("Unknown opcode.")
105             self.active = False
106             return
107
108         getattr(self, OPCODE_HANDLERS[opcode])()
109
110         self.active = False
111
112     def _get_register_name(self, reg_addr):
113         if (self.bsel0 is None) or (self.bsel1 is None):
114             # We don't know the bank we're in yet.
115             return None
116         else:
117             bank = (self.bsel1 << 1) + self.bsel0
118             return REGS[bank][reg_addr]
119
120     def _put_register_header(self):
121         reg_addr = self.mosi[0] & REG_ADDR_MASK
122         reg_name = self._get_register_name(reg_addr)
123
124         ss, es = self.cmd_ss, self.ranges[1][0]
125
126         if reg_name is None:
127             # We don't know the bank we're in yet.
128             self.put(ss, es, self.ann, [
129                      ANN_REG_ADDR,
130                      [
131                         'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
132                         '?:{0:02X}'.format(reg_addr),
133                      ]])
134             self.put(ss, es, self.ann, [
135                      ANN_WARNING,
136                      [
137                         'Warning: Register bank not known yet.',
138                         'Warning',
139                      ]])
140         else:
141             self.put(ss, es, self.ann, [
142                      ANN_REG_ADDR,
143                      [
144                         'Reg {0}'.format(reg_name),
145                         '{0}'.format(reg_name),
146                      ]])
147
148             if (reg_name == '-') or (reg_name == 'Reserved'):
149                 self.put(ss, es, self.ann, [
150                          ANN_WARNING,
151                          [
152                             'Warning: Invalid register accessed.',
153                             'Warning',
154                          ]])
155
156     def _put_data_byte(self, data, byte_index, binary=False):
157         ss = self.ranges[byte_index][0]
158         if byte_index == len(self.mosi) - 1:
159             es = self.cmd_es
160         else:
161             es = self.ranges[byte_index + 1][0]
162
163         if binary:
164             self.put(ss, es, self.ann, [
165                      ANN_DATA,
166                      [
167                         'Data 0b{0:08b}'.format(data),
168                         '{0:08b}'.format(data),
169                      ]])
170         else:
171             self.put(ss, es, self.ann, [
172                      ANN_DATA,
173                      [
174                         'Data 0x{0:02X}'.format(data),
175                         '{0:02X}'.format(data),
176                      ]])
177
178     def _put_command_warning(self, reason):
179         self.putc([
180                  ANN_WARNING,
181                  [
182                     'Warning: {0}'.format(reason),
183                     'Warning',
184                  ]])
185
186     def _process_rcr(self):
187         self.putc([ANN_RCR, ['Read Control Register', 'RCR']])
188
189         if (len(self.mosi) != 2) and (len(self.mosi) != 3):
190             self._put_command_warning('Invalid command length.')
191             return
192
193         self._put_register_header()
194
195         reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
196         if reg_name is None:
197             # We can't tell if we're accessing MAC/MII registers or not
198             # Let's trust the user in this case.
199             pass
200         else:
201             if (reg_name[0] == 'M') and (len(self.mosi) != 3):
202                 self._put_command_warning('Attempting to read a MAC/MII '
203                     + 'register without using the dummy byte.')
204                 return
205
206             if (reg_name[0] != 'M') and (len(self.mosi) != 2):
207                 self._put_command_warning('Attempting to read a non-MAC/MII '
208                                           + 'register using the dummy byte.')
209                 return
210
211         if len(self.mosi) == 2:
212             self._put_data_byte(self.miso[1], 1)
213         else:
214             ss, es = self.ranges[1][0], self.ranges[2][0]
215             self.put(ss, es, self.ann, [
216                      ANN_DATA,
217                      [
218                         'Dummy Byte',
219                         'Dummy',
220                      ]])
221             self._put_data_byte(self.miso[2], 2)
222
223     def _process_rbm(self):
224         if self.mosi[0] != 0b00111010:
225             self._put_command_warning('Invalid header byte.')
226             return
227
228         self.putc([
229                  ANN_RBM,
230                  [
231                     'Read Buffer Memory: Length {0}'.format(
232                         len(self.mosi) - 1),
233                     'RBM',
234                  ]])
235
236         for i in range(1, len(self.miso)):
237             self._put_data_byte(self.miso[i], i)
238
239     def _process_wcr(self):
240         self.putc([ANN_WCR, ['Write Control Register', 'WCR']])
241
242         if len(self.mosi) != 2:
243             self._put_command_warning('Invalid command length.')
244             return
245
246         self._put_register_header()
247         self._put_data_byte(self.mosi[1], 1)
248
249         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
250             self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
251             self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
252
253     def _process_wbm(self):
254         if self.mosi[0] != 0b01111010:
255             self._put_command_warning('Invalid header byte.')
256             return
257
258         self.putc([
259                  ANN_WBM,
260                  [
261                     'Write Buffer Memory: Length {0}'.format(
262                         len(self.mosi) - 1),
263                     'WBM',
264                  ]])
265
266         for i in range(1, len(self.mosi)):
267             self._put_data_byte(self.mosi[i], i)
268
269     def _process_bfc(self):
270         self.putc([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.putc([ANN_BFS, ['Bit Field Set', 'BFS']])
287
288         if len(self.mosi) != 2:
289             self._put_command_warning('Invalid command length.')
290             return
291
292         self._put_register_header()
293         self._put_data_byte(self.mosi[1], 1, True)
294
295         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
296             if self.mosi[1] & BIT_ECON1_BSEL0:
297                 self.bsel0 = 1
298             if self.mosi[1] & BIT_ECON1_BSEL1:
299                 self.bsel1 = 1
300
301     def _process_src(self):
302         self.putc([ANN_SRC, ['System Reset Command', 'SRC']])
303
304         if len(self.mosi) != 1:
305             self._put_command_warning('Invalid command length.')
306             return
307
308         self.bsel0 = 0
309         self.bsel1 = 0
310
311     def decode(self, ss, es, data):
312         ptype, data1, data2 = data
313
314         if ptype == 'CS-CHANGE':
315             new_cs = data2
316
317             if new_cs == 0:
318                 self.active = True
319                 self.cmd_ss = ss
320                 self.mosi = []
321                 self.miso = []
322                 self.ranges = []
323             elif new_cs == 1:
324                 if self.active:
325                     self.cmd_es = es
326                     self._process_command()
327         elif ptype == 'DATA':
328             mosi, miso = data1, data2
329
330             self.mosi.append(mosi)
331             self.miso.append(miso)
332             self.ranges.append((ss, es))