]> sigrok.org Git - libsigrokdecode.git/blob - decoders/enc28j60/pd.py
decoders: Use a slightly more consistent/logical annotation row setup.
[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         ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
71         ('commands', 'Commands',
72             (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
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.out_ann = self.register(srd.OUTPUT_ANN)
93
94     def putc(self, data):
95         self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
96
97     def putr(self, data):
98         self.put(self.range_ss, self.range_es, self.out_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([ANN_REG_ADDR, [
134                 'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
135                 '?:{0:02X}'.format(reg_addr)]])
136             self.putr([ANN_WARNING, ['Warning: Register bank not known yet.',
137                                      'Warning']])
138         else:
139             self.putr([ANN_REG_ADDR, ['Reg {0}'.format(reg_name),
140                                       '{0}'.format(reg_name)]])
141
142             if (reg_name == '-') or (reg_name == 'Reserved'):
143                 self.putr([ANN_WARNING, ['Warning: Invalid register accessed.',
144                                          'Warning']])
145
146     def _put_data_byte(self, data, byte_index, binary=False):
147         self.range_ss = self.ranges[byte_index][0]
148         if byte_index == len(self.mosi) - 1:
149             self.range_es = self.cmd_es
150         else:
151             self.range_es = self.ranges[byte_index + 1][0]
152
153         if binary:
154             self.putr([ANN_DATA, ['Data 0b{0:08b}'.format(data),
155                                   '{0:08b}'.format(data)]])
156         else:
157             self.putr([ANN_DATA, ['Data 0x{0:02X}'.format(data),
158                                   '{0:02X}'.format(data)]])
159
160     def _put_command_warning(self, reason):
161         self.putc([ANN_WARNING, ['Warning: {0}'.format(reason), 'Warning']])
162
163     def _process_rcr(self):
164         self.putc([ANN_RCR, ['Read Control Register', 'RCR']])
165
166         if (len(self.mosi) != 2) and (len(self.mosi) != 3):
167             self._put_command_warning('Invalid command length.')
168             return
169
170         self._put_register_header()
171
172         reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
173         if reg_name is None:
174             # We can't tell if we're accessing MAC/MII registers or not
175             # Let's trust the user in this case.
176             pass
177         else:
178             if (reg_name[0] == 'M') and (len(self.mosi) != 3):
179                 self._put_command_warning('Attempting to read a MAC/MII '
180                     + 'register without using the dummy byte.')
181                 return
182
183             if (reg_name[0] != 'M') and (len(self.mosi) != 2):
184                 self._put_command_warning('Attempting to read a non-MAC/MII '
185                                           + 'register using the dummy byte.')
186                 return
187
188         if len(self.mosi) == 2:
189             self._put_data_byte(self.miso[1], 1)
190         else:
191             self.range_ss, self.range_es = self.ranges[1][0], self.ranges[2][0]
192             self.putr([ANN_DATA, ['Dummy Byte', 'Dummy']])
193             self._put_data_byte(self.miso[2], 2)
194
195     def _process_rbm(self):
196         if self.mosi[0] != 0b00111010:
197             self._put_command_warning('Invalid header byte.')
198             return
199
200         self.putc([ANN_RBM, ['Read Buffer Memory: Length {0}'.format(
201                              len(self.mosi) - 1), 'RBM']])
202
203         for i in range(1, len(self.miso)):
204             self._put_data_byte(self.miso[i], i)
205
206     def _process_wcr(self):
207         self.putc([ANN_WCR, ['Write Control Register', 'WCR']])
208
209         if len(self.mosi) != 2:
210             self._put_command_warning('Invalid command length.')
211             return
212
213         self._put_register_header()
214         self._put_data_byte(self.mosi[1], 1)
215
216         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
217             self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
218             self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
219
220     def _process_wbm(self):
221         if self.mosi[0] != 0b01111010:
222             self._put_command_warning('Invalid header byte.')
223             return
224
225         self.putc([ANN_WBM, ['Write Buffer Memory: Length {0}'.format(
226                              len(self.mosi) - 1), 'WBM']])
227
228         for i in range(1, len(self.mosi)):
229             self._put_data_byte(self.mosi[i], i)
230
231     def _process_bfc(self):
232         self.putc([ANN_BFC, ['Bit Field Clear', 'BFC']])
233
234         if len(self.mosi) != 2:
235             self._put_command_warning('Invalid command length.')
236             return
237
238         self._put_register_header()
239         self._put_data_byte(self.mosi[1], 1, True)
240
241         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
242             if self.mosi[1] & BIT_ECON1_BSEL0:
243                 self.bsel0 = 0
244             if self.mosi[1] & BIT_ECON1_BSEL1:
245                 self.bsel1 = 0
246
247     def _process_bfs(self):
248         self.putc([ANN_BFS, ['Bit Field Set', 'BFS']])
249
250         if len(self.mosi) != 2:
251             self._put_command_warning('Invalid command length.')
252             return
253
254         self._put_register_header()
255         self._put_data_byte(self.mosi[1], 1, True)
256
257         if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
258             if self.mosi[1] & BIT_ECON1_BSEL0:
259                 self.bsel0 = 1
260             if self.mosi[1] & BIT_ECON1_BSEL1:
261                 self.bsel1 = 1
262
263     def _process_src(self):
264         self.putc([ANN_SRC, ['System Reset Command', 'SRC']])
265
266         if len(self.mosi) != 1:
267             self._put_command_warning('Invalid command length.')
268             return
269
270         self.bsel0 = 0
271         self.bsel1 = 0
272
273     def decode(self, ss, es, data):
274         ptype, data1, data2 = data
275
276         if ptype == 'CS-CHANGE':
277             new_cs = data2
278
279             if new_cs == 0:
280                 self.active = True
281                 self.cmd_ss = ss
282                 self.mosi = []
283                 self.miso = []
284                 self.ranges = []
285             elif new_cs == 1:
286                 if self.active:
287                     self.cmd_es = es
288                     self._process_command()
289         elif ptype == 'DATA':
290             mosi, miso = data1, data2
291
292             self.mosi.append(mosi)
293             self.miso.append(miso)
294             self.ranges.append((ss, es))