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