]>
Commit | Line | Data |
---|---|---|
c5a2f0b4 SA |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net> | |
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 | |
17 | ## along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | ## | |
19 | ||
20 | import sigrokdecode as srd | |
21 | from common.srdhelper import bitpack | |
22 | import decimal | |
23 | ||
24 | ''' | |
25 | OUTPUT_PYTHON format: | |
26 | ||
c9d548c7 SA |
27 | [ss, es, data] where data is a data byte of the LFAST payload. All bytes of |
28 | the payload are sent at once, each with its start and end sample. | |
c5a2f0b4 SA |
29 | ''' |
30 | ||
4d8b4da7 SA |
31 | # See tc27xD_um_v2.2.pdf, Table 20-10 |
32 | payload_sizes = { | |
33 | 0b000: '8 bit', | |
c9d548c7 SA |
34 | 0b001: '32 bit / 4 byte', |
35 | 0b010: '64 bit / 8 byte', | |
36 | 0b011: '96 bit / 12 byte', | |
37 | 0b100: '128 bit / 16 byte', | |
38 | 0b101: '256 bit / 32 byte', | |
39 | 0b110: '512 bit / 64 byte', | |
40 | 0b111: '288 bit / 36 byte' | |
4d8b4da7 SA |
41 | } |
42 | ||
43 | # See tc27xD_um_v2.2.pdf, Table 20-10 | |
44 | payload_byte_sizes = { | |
45 | 0b000: 1, | |
46 | 0b001: 4, | |
47 | 0b010: 8, | |
48 | 0b011: 12, | |
49 | 0b100: 16, | |
50 | 0b101: 32, | |
51 | 0b110: 64, | |
52 | 0b111: 36 | |
53 | } | |
54 | ||
55 | # See tc27xD_um_v2.2.pdf, Table 20-11 | |
56 | channel_types = { | |
c9d548c7 | 57 | 0b0000: 'Interface Control / PING', |
4d8b4da7 SA |
58 | 0b0001: 'Unsolicited Status (32 bit)', |
59 | 0b0010: 'Slave Interface Control / Read', | |
60 | 0b0011: 'CTS Transfer', | |
61 | 0b0100: 'Data Channel A', | |
62 | 0b0101: 'Data Channel B', | |
63 | 0b0110: 'Data Channel C', | |
64 | 0b0111: 'Data Channel D', | |
65 | 0b1000: 'Data Channel E', | |
66 | 0b1001: 'Data Channel F', | |
67 | 0b1010: 'Data Channel G', | |
68 | 0b1011: 'Data Channel H', | |
69 | 0b1100: 'Reserved', | |
70 | 0b1101: 'Reserved', | |
71 | 0b1110: 'Reserved', | |
72 | 0b1111: 'Reserved', | |
73 | } | |
74 | ||
75 | # See tc27xD_um_v2.2.pdf, Table 20-12 | |
76 | control_payloads = { | |
77 | 0x00: 'PING', | |
78 | 0x01: 'Reserved', | |
79 | 0x02: 'Slave interface clock multiplier start', | |
80 | 0x04: 'Slave interface clock multiplier stop', | |
81 | 0x08: 'Use 5 MBaud for M->S', | |
82 | 0x10: 'Use 320 MBaud for M->S', | |
83 | 0x20: 'Use 5 MBaud for S->M', | |
84 | 0x40: 'Use 20 MBaud for S->M (needs 20 MHz SysClk)', | |
85 | 0x80: 'Use 320 MBaud for S->M', | |
86 | 0x31: 'Enable slave interface transmitter', | |
87 | 0x32: 'Disable slave interface transmitter', | |
88 | 0x34: 'Enable clock test mode', | |
89 | 0x38: 'Disable clock test mode and payload loopback', | |
90 | 0xFF: 'Enable payload loopback', | |
91 | } | |
92 | ||
93 | ||
94 | ann_bit, ann_sync, ann_header_pl_size, ann_header_ch_type, ann_header_cts, \ | |
95 | ann_payload, ann_control_data, ann_sleepbit, ann_warning = range(9) | |
96 | state_sync, state_header, state_payload, state_sleepbit = range(4) | |
c5a2f0b4 SA |
97 | |
98 | class Decoder(srd.Decoder): | |
99 | api_version = 3 | |
100 | id = 'lfast' | |
101 | name = 'LFAST' | |
102 | longname = 'NXP LFAST interface' | |
103 | desc = 'Differential high-speed P2P interface' | |
104 | license = 'gplv2+' | |
105 | inputs = ['logic'] | |
106 | outputs = ['lfast'] | |
107 | tags = ['Embedded/industrial'] | |
108 | channels = ( | |
109 | {'id': 'data', 'name': 'Data', 'desc': 'TXP or RXP'}, | |
110 | ) | |
111 | annotations = ( | |
112 | ('bit', 'Bits'), | |
113 | ('sync', 'Sync Pattern'), | |
4d8b4da7 SA |
114 | ('header_pl_size', 'Payload Size'), |
115 | ('header_ch_type', 'Logical Channel Type'), | |
116 | ('header_cts', 'Clear To Send'), | |
c5a2f0b4 | 117 | ('payload', 'Payload'), |
4d8b4da7 SA |
118 | ('ctrl_data', 'Control Data'), |
119 | ('sleep', 'Sleep Bit'), | |
c5a2f0b4 SA |
120 | ('warning', 'Warning'), |
121 | ) | |
122 | annotation_rows = ( | |
123 | ('bits', 'Bits', (ann_bit,)), | |
4d8b4da7 SA |
124 | ('fields', 'Fields', (ann_sync, ann_header_pl_size, ann_header_ch_type, |
125 | ann_header_cts, ann_payload, ann_control_data, ann_sleepbit,)), | |
c5a2f0b4 SA |
126 | ('warnings', 'Warnings', (ann_warning,)), |
127 | ) | |
128 | ||
129 | def __init__(self): | |
130 | decimal.getcontext().rounding = decimal.ROUND_HALF_UP | |
c9d548c7 | 131 | self.bit_len = 0xFFFFFFFF |
c5a2f0b4 SA |
132 | self.reset() |
133 | ||
134 | def reset(self): | |
c9d548c7 | 135 | self.prev_bit_len = self.bit_len |
c5a2f0b4 SA |
136 | self.ss = self.es = 0 |
137 | self.ss_payload = self.es_payload = 0 | |
c9d548c7 | 138 | self.ss_byte = 0 |
c5a2f0b4 SA |
139 | self.bits = [] |
140 | self.payload = [] | |
4d8b4da7 SA |
141 | self.payload_size = 0 # Expected number of bytes, as read from header |
142 | self.bit_len = 0 # Length of one bit time, in samples | |
c9d548c7 | 143 | self.timeout = 0 # Desired timeout for next edge, in samples |
4d8b4da7 | 144 | self.ch_type_id = 0 # ID of channel type |
c5a2f0b4 SA |
145 | self.state = state_sync |
146 | ||
147 | def metadata(self, key, value): | |
148 | pass | |
149 | ||
150 | def start(self): | |
151 | self.out_python = self.register(srd.OUTPUT_PYTHON) | |
152 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
153 | ||
154 | def put_ann(self, ss, es, ann_class, value): | |
155 | self.put(ss, es, self.out_ann, [ann_class, value]) | |
156 | ||
157 | def put_payload(self): | |
158 | self.put(self.ss_payload, self.es_payload, self.out_python, self.payload) | |
159 | ||
160 | def handle_sync(self): | |
161 | if len(self.bits) == 1: | |
162 | self.ss_sync = self.ss_bit | |
163 | ||
164 | if len(self.bits) == 16: | |
165 | value = bitpack(self.bits) | |
166 | if value == 0xA84B: | |
167 | self.put_ann(self.ss_sync, self.es_bit, ann_sync, ['Sync OK']) | |
168 | else: | |
4d8b4da7 | 169 | self.put_ann(self.ss_sync, self.es_bit, ann_warning, ['Wrong Sync Value: {:02X}'.format(value)]) |
c9d548c7 | 170 | self.reset() |
c5a2f0b4 | 171 | |
c9d548c7 SA |
172 | # Only continue if we didn't just reset |
173 | if self.ss > 0: | |
174 | self.bits = [] | |
175 | self.state = state_header | |
176 | self.timeout = int(9.4 * self.bit_len) | |
c5a2f0b4 SA |
177 | |
178 | def handle_header(self): | |
179 | if len(self.bits) == 1: | |
180 | self.ss_header = self.ss_bit | |
181 | ||
182 | if len(self.bits) == 8: | |
4d8b4da7 SA |
183 | # See tc27xD_um_v2.2.pdf, Figure 20-47, for the header structure |
184 | bit_len = (self.es_bit - self.ss_header) / 8 | |
c5a2f0b4 | 185 | value = bitpack(self.bits) |
4d8b4da7 SA |
186 | |
187 | ss = self.ss_header | |
c9d548c7 | 188 | es = ss + 3 * bit_len |
4d8b4da7 SA |
189 | size_id = (value & 0xE0) >> 5 |
190 | size = payload_sizes.get(size_id) | |
c9d548c7 | 191 | self.payload_size = payload_byte_sizes.get(size_id) |
4d8b4da7 SA |
192 | self.put_ann(int(ss), int(es), ann_header_pl_size, [size]) |
193 | ||
194 | ss = es | |
c9d548c7 | 195 | es = ss + 4 * bit_len |
4d8b4da7 SA |
196 | self.ch_type_id = (value & 0x1E) >> 1 |
197 | ch_type = channel_types.get(self.ch_type_id) | |
198 | self.put_ann(int(ss), int(es), ann_header_ch_type, [ch_type]) | |
199 | ||
200 | ss = es | |
c9d548c7 SA |
201 | es = ss + bit_len |
202 | cts = value & 0x01 | |
4d8b4da7 SA |
203 | self.put_ann(int(ss), int(es), ann_header_cts, ['{}'.format(cts)]) |
204 | ||
c5a2f0b4 SA |
205 | self.bits = [] |
206 | self.state = state_payload | |
c9d548c7 | 207 | self.timeout = int(9.4 * self.bit_len) |
c5a2f0b4 SA |
208 | |
209 | def handle_payload(self): | |
c9d548c7 SA |
210 | self.timeout = int((self.payload_size - len(self.payload)) * 8 * self.bit_len) |
211 | ||
c5a2f0b4 SA |
212 | if len(self.bits) == 1: |
213 | self.ss_byte = self.ss_bit | |
214 | if self.ss_payload == 0: | |
215 | self.ss_payload = self.ss_bit | |
216 | ||
217 | if len(self.bits) == 8: | |
218 | value = bitpack(self.bits) | |
4d8b4da7 | 219 | value_hex = '{:02X}'.format(value) |
c9d548c7 | 220 | |
4d8b4da7 | 221 | # Control transfers have no SIPI payload, show them as control transfers |
c9d548c7 | 222 | # Check the channel_types list for the meaning of the magic values |
4d8b4da7 SA |
223 | if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011): |
224 | self.put_ann(self.ss_byte, self.es_bit, ann_payload, [value_hex]) | |
225 | else: | |
226 | # Control transfers are 8-bit transfers, so only evaluate the first byte | |
227 | if len(self.payload) == 0: | |
228 | ctrl_data = control_payloads.get(value, value_hex) | |
229 | self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [ctrl_data]) | |
230 | else: | |
231 | self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [value_hex]) | |
232 | ||
c5a2f0b4 | 233 | self.bits = [] |
c5a2f0b4 | 234 | self.es_payload = self.es_bit |
c9d548c7 SA |
235 | self.payload.append((self.ss_byte, self.es_payload, value)) |
236 | ||
237 | if (len(self.payload) == self.payload_size): | |
238 | self.timeout = int(1.4 * self.bit_len) | |
239 | self.state = state_sleepbit | |
c5a2f0b4 | 240 | |
4d8b4da7 SA |
241 | def handle_sleepbit(self): |
242 | if len(self.bits) == 0: | |
243 | self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N']) | |
244 | elif len(self.bits) > 1: | |
245 | self.put_ann(self.ss_bit, self.es_bit, ann_warning, ['Expected only the sleep bit, got {} bits instead'.format(len(self.bits))]) | |
c5a2f0b4 | 246 | else: |
4d8b4da7 SA |
247 | if self.bits[0] == 1: |
248 | self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['LVDS sleep mode request', 'Sleep', 'Y']) | |
c5a2f0b4 | 249 | else: |
4d8b4da7 | 250 | self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N']) |
c5a2f0b4 | 251 | |
4d8b4da7 | 252 | # We only send the payload out if this is an actual data transfer; |
c9d548c7 | 253 | # check the channel_types list for the meaning of the magic values |
4d8b4da7 SA |
254 | if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011): |
255 | if len(self.payload) > 0: | |
256 | self.put_payload() | |
257 | ||
c5a2f0b4 SA |
258 | def decode(self): |
259 | while True: | |
260 | if self.timeout == 0: | |
261 | rising_edge, = self.wait({0: 'e'}) | |
262 | else: | |
263 | rising_edge, = self.wait([{0: 'e'}, {'skip': self.timeout}]) | |
264 | ||
4d8b4da7 | 265 | # If this is the first edge, we only update ss |
c5a2f0b4 SA |
266 | if self.ss == 0: |
267 | self.ss = self.samplenum | |
c9d548c7 SA |
268 | # Let's set the timeout for the sync pattern as well |
269 | self.timeout = int(16.2 * self.prev_bit_len) | |
c5a2f0b4 | 270 | continue |
c9d548c7 | 271 | |
c5a2f0b4 SA |
272 | self.es = self.samplenum |
273 | ||
4d8b4da7 | 274 | # Check for the sleep bit if this is a timeout condition |
c9d548c7 | 275 | if (len(self.matched) == 2) and self.matched[1]: |
4d8b4da7 | 276 | rising_edge = ~rising_edge |
c9d548c7 SA |
277 | if self.state == state_sync: |
278 | self.reset() | |
279 | continue | |
280 | elif self.state == state_sleepbit: | |
281 | self.ss_bit += self.bit_len | |
282 | self.es_bit = self.ss_bit + self.bit_len | |
4d8b4da7 | 283 | self.handle_sleepbit() |
c9d548c7 SA |
284 | self.reset() |
285 | continue | |
286 | ||
287 | # Shouldn't happen but we check just in case | |
288 | if int(self.es - self.ss) == 0: | |
289 | continue | |
c5a2f0b4 SA |
290 | |
291 | # We use the first bit to deduce the bit length | |
292 | if self.bit_len == 0: | |
293 | self.bit_len = self.es - self.ss | |
294 | ||
295 | # Determine number of bits covered by this edge | |
296 | bit_count = (self.es - self.ss) / self.bit_len | |
297 | bit_count = int(decimal.Decimal(bit_count).to_integral_value()) | |
c9d548c7 | 298 | |
4d8b4da7 SA |
299 | if bit_count == 0: |
300 | self.put_ann(self.ss, self.es, ann_warning, ['Bit time too short']) | |
301 | self.reset() | |
302 | continue | |
c9d548c7 | 303 | |
c5a2f0b4 SA |
304 | bit_value = '0' if rising_edge else '1' |
305 | ||
306 | divided_len = (self.es - self.ss) / bit_count | |
307 | for i in range(bit_count): | |
308 | self.ss_bit = int(self.ss + i * divided_len) | |
309 | self.es_bit = int(self.ss_bit + divided_len) | |
310 | self.put_ann(self.ss_bit, self.es_bit, ann_bit, [bit_value]) | |
311 | ||
312 | # Place the new bit at the front of the bit list | |
313 | self.bits.insert(0, (0 if rising_edge else 1)) | |
314 | ||
315 | if self.state == state_sync: | |
316 | self.handle_sync() | |
317 | elif self.state == state_header: | |
318 | self.handle_header() | |
319 | elif self.state == state_payload: | |
320 | self.handle_payload() | |
4d8b4da7 SA |
321 | elif self.state == state_sleepbit: |
322 | self.handle_sleepbit() | |
c9d548c7 SA |
323 | self.reset() |
324 | ||
325 | if self.ss == 0: | |
326 | break # Because reset() was called, invalidating everything | |
c5a2f0b4 | 327 | |
4d8b4da7 SA |
328 | # Only update ss if we didn't just perform a reset |
329 | if self.ss > 0: | |
330 | self.ss = self.samplenum | |
c9d548c7 SA |
331 | |
332 | # If we got here when a timeout occurred, we have processed all null | |
333 | # bits that we could and should reset now to find the next packet | |
334 | if (len(self.matched) == 2) and self.matched[1]: | |
335 | self.reset() |