]>
Commit | Line | Data |
---|---|---|
c4d52210 UH |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de> | |
5 | ## | |
6 | ## This program is free software; you can redistribute it and/or modify | |
7 | ## it under the terms of the GNU General Public License as published by | |
8 | ## the Free Software Foundation; either version 2 of the License, or | |
9 | ## (at your option) any later version. | |
10 | ## | |
11 | ## This program is distributed in the hope that it will be useful, | |
12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | ## GNU General Public License for more details. | |
15 | ## | |
16 | ## You should have received a copy of the GNU General Public License | |
4539e9ca | 17 | ## along with this program; if not, see <http://www.gnu.org/licenses/>. |
c4d52210 UH |
18 | ## |
19 | ||
42fb0f33 | 20 | import copy |
c4d52210 UH |
21 | import sigrokdecode as srd |
22 | from .lists import * | |
23 | ||
24 | class Decoder(srd.Decoder): | |
b197383c | 25 | api_version = 3 |
c4d52210 UH |
26 | id = 'eeprom24xx' |
27 | name = '24xx EEPROM' | |
28 | longname = '24xx I²C EEPROM' | |
29 | desc = '24xx series I²C EEPROM protocol.' | |
30 | license = 'gplv2+' | |
31 | inputs = ['i2c'] | |
6cbba91f | 32 | outputs = [] |
d6d8a8a4 | 33 | tags = ['IC', 'Memory'] |
c4d52210 UH |
34 | options = ( |
35 | {'id': 'chip', 'desc': 'Chip', 'default': 'generic', | |
36 | 'values': tuple(chips.keys())}, | |
37 | {'id': 'addr_counter', 'desc': 'Initial address counter value', | |
38 | 'default': 0}, | |
39 | ) | |
40 | annotations = ( | |
41 | # Warnings | |
e144452b | 42 | ('warning', 'Warning'), |
c4d52210 UH |
43 | # Bits/bytes |
44 | ('control-code', 'Control code'), | |
45 | ('address-pin', 'Address pin (A0/A1/A2)'), | |
46 | ('rw-bit', 'Read/write bit'), | |
47 | ('word-addr-byte', 'Word address byte'), | |
48 | ('data-byte', 'Data byte'), | |
49 | # Fields | |
50 | ('control-word', 'Control word'), | |
51 | ('word-addr', 'Word address'), | |
52 | ('data', 'Data'), | |
53 | # Operations | |
54 | ('byte-write', 'Byte write'), | |
55 | ('page-write', 'Page write'), | |
56 | ('cur-addr-read', 'Current address read'), | |
57 | ('random-read', 'Random read'), | |
58 | ('seq-random-read', 'Sequential random read'), | |
59 | ('seq-cur-addr-read', 'Sequential current address read'), | |
60 | ('ack-polling', 'Acknowledge polling'), | |
61 | ('set-bank-addr', 'Set bank address'), # SBA. Only 34AA04. | |
62 | ('read-bank-addr', 'Read bank address'), # RBA. Only 34AA04. | |
63 | ('set-wp', 'Set write protection'), # SWP | |
64 | ('clear-all-wp', 'Clear all write protection'), # CWP | |
65 | ('read-wp', 'Read write protection status'), # RPS | |
66 | ) | |
67 | annotation_rows = ( | |
68 | ('bits-bytes', 'Bits/bytes', (1, 2, 3, 4, 5)), | |
69 | ('fields', 'Fields', (6, 7, 8)), | |
70 | ('ops', 'Operations', tuple(range(9, 21))), | |
71 | ('warnings', 'Warnings', (0,)), | |
72 | ) | |
73 | binary = ( | |
74 | ('binary', 'Binary'), | |
75 | ) | |
76 | ||
92b7b49f | 77 | def __init__(self): |
10aeb8ea GS |
78 | self.reset() |
79 | ||
80 | def reset(self): | |
0f4d8807 | 81 | self.reset_variables() |
c4d52210 UH |
82 | |
83 | def start(self): | |
84 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
2f370328 | 85 | self.out_binary = self.register(srd.OUTPUT_BINARY) |
c4d52210 UH |
86 | self.chip = chips[self.options['chip']] |
87 | self.addr_counter = self.options['addr_counter'] | |
88 | ||
89 | def putb(self, data): | |
90 | self.put(self.ss_block, self.es_block, self.out_ann, data) | |
91 | ||
92 | def putbin(self, data): | |
2f370328 | 93 | self.put(self.ss_block, self.es_block, self.out_binary, data) |
c4d52210 UH |
94 | |
95 | def putbits(self, bit1, bit2, bits, data): | |
96 | self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data) | |
97 | ||
0f4d8807 | 98 | def reset_variables(self): |
c4d52210 UH |
99 | self.state = 'WAIT FOR START' |
100 | self.packets = [] | |
101 | self.bytebuf = [] | |
102 | self.is_cur_addr_read = False | |
103 | self.is_random_access_read = False | |
104 | self.is_seq_random_read = False | |
105 | self.is_byte_write = False | |
106 | self.is_page_write = False | |
107 | ||
108 | def packet_append(self): | |
109 | self.packets.append([self.ss, self.es, self.cmd, self.databyte, self.bits]) | |
110 | if self.cmd in ('DATA READ', 'DATA WRITE'): | |
111 | self.bytebuf.append(self.databyte) | |
112 | ||
113 | def hexbytes(self, idx): | |
114 | return ' '.join(['%02X' % b for b in self.bytebuf[idx:]]) | |
115 | ||
116 | def put_control_word(self, bits): | |
117 | s = ''.join(['%d' % b[0] for b in reversed(bits[4:])]) | |
118 | self.putbits(7, 4, bits, [1, ['Control code bits: ' + s, | |
119 | 'Control code: ' + s, 'Ctrl code: ' + s, 'Ctrl code', 'Ctrl', 'C']]) | |
120 | for i in reversed(range(self.chip['addr_pins'])): | |
121 | self.putbits(i + 1, i + 1, bits, | |
122 | [2, ['Address bit %d: %d' % (i, bits[i + 1][0]), | |
123 | 'Addr bit %d' % i, 'A%d' % i, 'A']]) | |
124 | s1 = 'read' if bits[0][0] == 1 else 'write' | |
125 | s2 = 'R' if bits[0][0] == 1 else 'W' | |
126 | self.putbits(0, 0, bits, [3, ['R/W bit: ' + s1, 'R/W', 'RW', s2]]) | |
127 | self.putbits(7, 0, bits, [6, ['Control word', 'Control', 'CW', 'C']]) | |
128 | ||
129 | def put_word_addr(self, p): | |
130 | if self.chip['addr_bytes'] == 1: | |
131 | a = p[1][3] | |
132 | self.put(p[1][0], p[1][1], self.out_ann, | |
133 | [4, ['Word address byte: %02X' % a, 'Word addr byte: %02X' % a, | |
134 | 'Addr: %02X' % a, 'A: %02X' % a, '%02X' % a]]) | |
135 | self.put(p[1][0], p[1][1], self.out_ann, [7, ['Word address', | |
136 | 'Word addr', 'Addr', 'A']]) | |
137 | self.addr_counter = a | |
138 | else: | |
139 | a = p[1][3] | |
140 | self.put(p[1][0], p[1][1], self.out_ann, | |
141 | [4, ['Word address high byte: %02X' % a, | |
142 | 'Word addr high byte: %02X' % a, | |
143 | 'Addr high: %02X' % a, 'AH: %02X' % a, '%02X' % a]]) | |
144 | a = p[2][3] | |
145 | self.put(p[2][0], p[2][1], self.out_ann, | |
146 | [4, ['Word address low byte: %02X' % a, | |
147 | 'Word addr low byte: %02X' % a, | |
148 | 'Addr low: %02X' % a, 'AL: %02X' % a, '%02X' % a]]) | |
149 | self.put(p[1][0], p[2][1], self.out_ann, [7, ['Word address', | |
150 | 'Word addr', 'Addr', 'A']]) | |
151 | self.addr_counter = (p[1][3] << 8) | p[2][3] | |
152 | ||
153 | def put_data_byte(self, p): | |
154 | if self.chip['addr_bytes'] == 1: | |
155 | s = '%02X' % self.addr_counter | |
156 | else: | |
157 | s = '%04X' % self.addr_counter | |
158 | self.put(p[0], p[1], self.out_ann, [5, ['Data byte %s: %02X' % \ | |
159 | (s, p[3]), 'Data byte: %02X' % p[3], \ | |
160 | 'Byte: %02X' % p[3], 'DB: %02X' % p[3], '%02X' % p[3]]]) | |
161 | ||
162 | def put_data_bytes(self, idx, cls, s): | |
163 | for p in self.packets[idx:]: | |
164 | self.put_data_byte(p) | |
165 | self.addr_counter += 1 | |
166 | self.put(self.packets[idx][0], self.packets[-1][1], self.out_ann, | |
167 | [8, ['Data', 'D']]) | |
168 | a = ''.join(['%s' % c[0] for c in s.split()]).upper() | |
169 | self.putb([cls, ['%s (%s): %s' % (s, self.addr_and_len(), \ | |
170 | self.hexbytes(self.chip['addr_bytes'])), | |
171 | '%s (%s)' % (s, self.addr_and_len()), s, a, s[0]]]) | |
2824e811 | 172 | self.putbin([0, bytes(self.bytebuf[self.chip['addr_bytes']:])]) |
c4d52210 UH |
173 | |
174 | def addr_and_len(self): | |
175 | if self.chip['addr_bytes'] == 1: | |
176 | a = '%02X' % self.bytebuf[0] | |
177 | else: | |
178 | a = '%02X%02X' % tuple(self.bytebuf[:2]) | |
179 | num_data_bytes = len(self.bytebuf) - self.chip['addr_bytes'] | |
180 | d = '%d bytes' % num_data_bytes | |
181 | if num_data_bytes <= 1: | |
182 | d = d[:-1] | |
183 | return 'addr=%s, %s' % (a, d) | |
184 | ||
185 | def decide_on_seq_or_rnd_read(self): | |
186 | if len(self.bytebuf) < 2: | |
0f4d8807 | 187 | self.reset_variables() |
c4d52210 UH |
188 | return |
189 | if len(self.bytebuf) == 2: | |
190 | self.is_random_access_read = True | |
191 | else: | |
192 | self.is_seq_random_read = True | |
193 | ||
194 | def put_operation(self): | |
195 | idx = 1 + self.chip['addr_bytes'] | |
196 | if self.is_byte_write: | |
197 | # Byte write: word address, one data byte. | |
198 | self.put_word_addr(self.packets) | |
199 | self.put_data_bytes(idx, 9, 'Byte write') | |
200 | elif self.is_page_write: | |
201 | # Page write: word address, two or more data bytes. | |
202 | self.put_word_addr(self.packets) | |
203 | intitial_addr = self.addr_counter | |
204 | self.put_data_bytes(idx, 10, 'Page write') | |
205 | num_bytes_to_write = len(self.packets[idx:]) | |
206 | if num_bytes_to_write > self.chip['page_size']: | |
207 | self.putb([0, ['Warning: Wrote %d bytes but page size is ' | |
208 | 'only %d bytes!' % (num_bytes_to_write, | |
209 | self.chip['page_size'])]]) | |
210 | page1 = int(intitial_addr / self.chip['page_size']) | |
211 | page2 = int((self.addr_counter - 1) / self.chip['page_size']) | |
212 | if page1 != page2: | |
213 | self.putb([0, ['Warning: Page write crossed page boundary ' | |
214 | 'from page %d to %d!' % (page1, page2)]]) | |
215 | elif self.is_cur_addr_read: | |
216 | # Current address read: no word address, one data byte. | |
217 | self.put_data_byte(self.packets[1]) | |
218 | self.put(self.packets[1][0], self.packets[-1][1], self.out_ann, | |
219 | [8, ['Data', 'D']]) | |
220 | self.putb([11, ['Current address read: %02X' % self.bytebuf[0], | |
221 | 'Current address read', 'Cur addr read', 'CAR', 'C']]) | |
2824e811 | 222 | self.putbin([0, bytes([self.bytebuf[0]])]) |
c4d52210 UH |
223 | self.addr_counter += 1 |
224 | elif self.is_random_access_read: | |
225 | # Random access read: word address, one data byte. | |
226 | self.put_control_word(self.packets[idx][4]) | |
227 | self.put_word_addr(self.packets) | |
228 | self.put_data_bytes(idx + 1, 12, 'Random access read') | |
229 | elif self.is_seq_random_read: | |
230 | # Sequential random read: word address, two or more data bytes. | |
231 | self.put_control_word(self.packets[idx][4]) | |
232 | self.put_word_addr(self.packets) | |
233 | self.put_data_bytes(idx + 1, 13, 'Sequential random read') | |
234 | ||
235 | def handle_wait_for_start(self): | |
236 | # Wait for an I²C START condition. | |
237 | if self.cmd not in ('START', 'START REPEAT'): | |
238 | return | |
239 | self.ss_block = self.ss | |
240 | self.state = 'GET CONTROL WORD' | |
241 | ||
242 | def handle_get_control_word(self): | |
243 | # The packet after START must be an ADDRESS READ or ADDRESS WRITE. | |
244 | if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'): | |
0f4d8807 | 245 | self.reset_variables() |
c4d52210 UH |
246 | return |
247 | self.packet_append() | |
248 | self.put_control_word(self.bits) | |
249 | self.state = '%s GET ACK NACK AFTER CONTROL WORD' % self.cmd[8] | |
250 | ||
251 | def handle_r_get_ack_nack_after_control_word(self): | |
252 | if self.cmd == 'ACK': | |
253 | self.state = 'R GET WORD ADDR OR BYTE' | |
254 | elif self.cmd == 'NACK': | |
255 | self.es_block = self.es | |
256 | self.putb([0, ['Warning: No reply from slave!']]) | |
0f4d8807 | 257 | self.reset_variables() |
c4d52210 | 258 | else: |
0f4d8807 | 259 | self.reset_variables() |
c4d52210 UH |
260 | |
261 | def handle_r_get_word_addr_or_byte(self): | |
262 | if self.cmd == 'STOP': | |
263 | self.es_block = self.es | |
264 | self.putb([0, ['Warning: Slave replied, but master aborted!']]) | |
0f4d8807 | 265 | self.reset_variables() |
c4d52210 UH |
266 | return |
267 | elif self.cmd != 'DATA READ': | |
0f4d8807 | 268 | self.reset_variables() |
c4d52210 UH |
269 | return |
270 | self.packet_append() | |
271 | self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE' | |
272 | ||
273 | def handle_r_get_ack_nack_after_word_addr_or_byte(self): | |
274 | if self.cmd == 'ACK': | |
275 | self.state = 'R GET RESTART' | |
276 | elif self.cmd == 'NACK': | |
277 | self.is_cur_addr_read = True | |
278 | self.state = 'GET STOP AFTER LAST BYTE' | |
279 | else: | |
0f4d8807 | 280 | self.reset_variables() |
c4d52210 UH |
281 | |
282 | def handle_r_get_restart(self): | |
283 | if self.cmd == 'RESTART': | |
284 | self.state = 'R READ BYTE' | |
285 | else: | |
0f4d8807 | 286 | self.reset_variables() |
c4d52210 UH |
287 | |
288 | def handle_r_read_byte(self): | |
289 | if self.cmd == 'DATA READ': | |
290 | self.packet_append() | |
291 | self.state = 'R GET ACK NACK AFTER BYTE WAS READ' | |
292 | else: | |
0f4d8807 | 293 | self.reset_variables() |
c4d52210 UH |
294 | |
295 | def handle_r_get_ack_nack_after_byte_was_read(self): | |
296 | if self.cmd == 'ACK': | |
297 | self.state = 'R READ BYTE' | |
298 | elif self.cmd == 'NACK': | |
299 | # It's either a RANDOM READ or a SEQUENTIAL READ. | |
300 | self.state = 'GET STOP AFTER LAST BYTE' | |
301 | else: | |
0f4d8807 | 302 | self.reset_variables() |
c4d52210 UH |
303 | |
304 | def handle_w_get_ack_nack_after_control_word(self): | |
305 | if self.cmd == 'ACK': | |
306 | self.state = 'W GET WORD ADDR' | |
307 | elif self.cmd == 'NACK': | |
308 | self.es_block = self.es | |
309 | self.putb([0, ['Warning: No reply from slave!']]) | |
0f4d8807 | 310 | self.reset_variables() |
c4d52210 | 311 | else: |
0f4d8807 | 312 | self.reset_variables() |
c4d52210 UH |
313 | |
314 | def handle_w_get_word_addr(self): | |
315 | if self.cmd == 'STOP': | |
316 | self.es_block = self.es | |
317 | self.putb([0, ['Warning: Slave replied, but master aborted!']]) | |
0f4d8807 | 318 | self.reset_variables() |
c4d52210 UH |
319 | return |
320 | elif self.cmd != 'DATA WRITE': | |
0f4d8807 | 321 | self.reset_variables() |
c4d52210 UH |
322 | return |
323 | self.packet_append() | |
324 | self.state = 'W GET ACK AFTER WORD ADDR' | |
325 | ||
326 | def handle_w_get_ack_after_word_addr(self): | |
327 | if self.cmd == 'ACK': | |
328 | self.state = 'W DETERMINE EEPROM READ OR WRITE' | |
329 | else: | |
0f4d8807 | 330 | self.reset_variables() |
c4d52210 UH |
331 | |
332 | def handle_w_determine_eeprom_read_or_write(self): | |
333 | if self.cmd == 'START REPEAT': | |
334 | # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ. | |
335 | self.state = 'R2 GET CONTROL WORD' | |
336 | elif self.cmd == 'DATA WRITE': | |
337 | self.packet_append() | |
338 | self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN' | |
339 | else: | |
0f4d8807 | 340 | self.reset_variables() |
c4d52210 UH |
341 | |
342 | def handle_w_write_byte(self): | |
343 | if self.cmd == 'DATA WRITE': | |
344 | self.packet_append() | |
345 | self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN' | |
346 | elif self.cmd == 'STOP': | |
347 | if len(self.bytebuf) < 2: | |
0f4d8807 | 348 | self.reset_variables() |
c4d52210 UH |
349 | return |
350 | self.es_block = self.es | |
351 | if len(self.bytebuf) == 2: | |
352 | self.is_byte_write = True | |
353 | else: | |
354 | self.is_page_write = True | |
355 | self.put_operation() | |
0f4d8807 | 356 | self.reset_variables() |
c4d52210 UH |
357 | elif self.cmd == 'START REPEAT': |
358 | # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ. | |
359 | self.state = 'R2 GET CONTROL WORD' | |
360 | else: | |
0f4d8807 | 361 | self.reset_variables() |
c4d52210 UH |
362 | |
363 | def handle_w_get_ack_nack_after_byte_was_written(self): | |
364 | if self.cmd == 'ACK': | |
365 | self.state = 'W WRITE BYTE' | |
366 | else: | |
0f4d8807 | 367 | self.reset_variables() |
c4d52210 UH |
368 | |
369 | def handle_r2_get_control_word(self): | |
370 | if self.cmd == 'ADDRESS READ': | |
371 | self.packet_append() | |
372 | self.state = 'R2 GET ACK AFTER ADDR READ' | |
373 | else: | |
0f4d8807 | 374 | self.reset_variables() |
c4d52210 UH |
375 | |
376 | def handle_r2_get_ack_after_addr_read(self): | |
377 | if self.cmd == 'ACK': | |
378 | self.state = 'R2 READ BYTE' | |
379 | else: | |
0f4d8807 | 380 | self.reset_variables() |
c4d52210 UH |
381 | |
382 | def handle_r2_read_byte(self): | |
383 | if self.cmd == 'DATA READ': | |
384 | self.packet_append() | |
385 | self.state = 'R2 GET ACK NACK AFTER BYTE WAS READ' | |
386 | elif self.cmd == 'STOP': | |
387 | self.decide_on_seq_or_rnd_read() | |
388 | self.es_block = self.es | |
389 | self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']]) | |
390 | self.put_operation() | |
0f4d8807 | 391 | self.reset_variables() |
c4d52210 | 392 | else: |
0f4d8807 | 393 | self.reset_variables() |
c4d52210 UH |
394 | |
395 | def handle_r2_get_ack_nack_after_byte_was_read(self): | |
396 | if self.cmd == 'ACK': | |
397 | self.state = 'R2 READ BYTE' | |
398 | elif self.cmd == 'NACK': | |
399 | self.decide_on_seq_or_rnd_read() | |
400 | self.state = 'GET STOP AFTER LAST BYTE' | |
401 | else: | |
0f4d8807 | 402 | self.reset_variables() |
c4d52210 UH |
403 | |
404 | def handle_get_stop_after_last_byte(self): | |
405 | if self.cmd == 'STOP': | |
406 | self.es_block = self.es | |
407 | self.put_operation() | |
0f4d8807 | 408 | self.reset_variables() |
c4d52210 UH |
409 | elif self.cmd == 'START REPEAT': |
410 | self.es_block = self.es | |
411 | self.putb([0, ['Warning: STOP expected (not RESTART)']]) | |
412 | self.put_operation() | |
0f4d8807 | 413 | self.reset_variables() |
c4d52210 UH |
414 | self.ss_block = self.ss |
415 | self.state = 'GET CONTROL WORD' | |
416 | else: | |
0f4d8807 | 417 | self.reset_variables() |
c4d52210 UH |
418 | |
419 | def decode(self, ss, es, data): | |
42fb0f33 | 420 | cmd, _ = data |
c4d52210 UH |
421 | |
422 | # Collect the 'BITS' packet, then return. The next packet is | |
423 | # guaranteed to belong to these bits we just stored. | |
42fb0f33 GS |
424 | if cmd == 'BITS': |
425 | _, databits = data | |
426 | self.bits = copy.deepcopy(databits) | |
c4d52210 UH |
427 | return |
428 | ||
42fb0f33 GS |
429 | # Store the start/end samples of this I²C packet. Deep copy |
430 | # caller's data, assuming that implementation details of the | |
431 | # above complex methods can access the data after returning | |
432 | # from the .decode() invocation, with the data having become | |
433 | # invalid by that time of access. This conservative approach | |
434 | # can get weakened after close inspection of those methods. | |
c4d52210 | 435 | self.ss, self.es = ss, es |
42fb0f33 GS |
436 | _, databyte = data |
437 | databyte = copy.deepcopy(databyte) | |
438 | self.cmd, self.databyte = cmd, databyte | |
c4d52210 UH |
439 | |
440 | # State machine. | |
441 | s = 'handle_%s' % self.state.lower().replace(' ', '_') | |
442 | handle_state = getattr(self, s) | |
443 | handle_state() |