2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
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.
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.
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/>.
20 import sigrokdecode
as srd
21 from common
.srdhelper
import bitpack
30 - 'ITEM', [<item>, <itembitsize>]
31 - 'WORD', [<word>, <wordbitsize>, <worditemcount>]
34 - A single item (a number). It can be of arbitrary size. The max. number
35 of bits in this item is specified in <itembitsize>.
38 - The size of an item (in bits). For a 4-bit parallel bus this is 4,
39 for a 16-bit parallel bus this is 16, and so on.
42 - A single word (a number). It can be of arbitrary size. The max. number
43 of bits in this word is specified in <wordbitsize>. The (exact) number
44 of items in this word is specified in <worditemcount>.
47 - The size of a word (in bits). For a 2-item word with 8-bit items
48 <wordbitsize> is 16, for a 3-item word with 4-bit items <wordbitsize>
52 - The size of a word (in number of items). For a 4-item word (no matter
53 how many bits each item consists of) <worditemcount> is 4, for a 7-item
54 word <worditemcount> is 7, and so on.
62 DATA_N
= DATA_0
+ NUM_CHANNELS
63 # BEWARE! DATA_N points _beyond_ the data partition (Python range(3)
64 # semantics, useful to have to simplify other code locations).
68 ITEM
, WORD
, WARN
= range(3)
70 class ChannelError(Exception):
73 class Decoder(srd
.Decoder
):
77 longname
= 'Parallel sync bus'
78 desc
= 'Generic parallel synchronous bus.'
81 outputs
= ['parallel']
83 optional_channels
= tuple(
84 [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}] +
86 {'id': 'd%d' % i
, 'name': 'D%d' % i
, 'desc': 'Data line %d' % i
}
87 for i
in range(NUM_CHANNELS
)
89 [{'id': 'rst', 'name': 'RST', 'desc': 'RESET line'}]
92 {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
93 'default': 'rising', 'values': ('rising', 'falling', 'either')},
94 {'id': 'reset_polarity', 'desc': 'Reset line polarity',
95 'default': 'low-active', 'values': ('low-active', 'high-active')},
96 {'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
98 {'id': 'endianness', 'desc': 'Data endianness',
99 'default': 'little', 'values': ('little', 'big')},
104 ('warning', 'Warning'),
107 ('items', 'Items', (Ann
.ITEM
,)),
108 ('words', 'Words', (Ann
.WORD
,)),
109 ('warnings', 'Warnings', (Ann
.WARN
,)),
112 ('binary', 'Binary'),
119 self
.pend_item
= None
123 self
.out_python
= self
.register(srd
.OUTPUT_PYTHON
)
124 self
.out_binary
= self
.register(srd
.OUTPUT_BINARY
)
125 self
.out_ann
= self
.register(srd
.OUTPUT_ANN
)
127 def putg(self
, ss
, es
, ann
, txts
):
128 self
.put(ss
, es
, self
.out_ann
, [ann
, txts
])
130 def putpy(self
, ss
, es
, ann
, data
):
131 self
.put(ss
, es
, self
.out_python
, [ann
, data
])
133 def putbin(self
, ss
, es
, ann_class
, data
):
134 self
.put(ss
, es
, self
.out_binary
, [ann_class
, data
])
136 def flush_word(self
, bus_width
):
137 if not self
.word_items
:
139 word_size
= self
.options
['wordsize']
141 items
= self
.word_items
142 ss
, es
= items
[0][0], items
[-1][1]
143 items
= [i
[2] for i
in items
]
144 if self
.options
['endianness'] == 'big':
146 word
= sum([d
<< (i
* bus_width
) for i
, d
in enumerate(items
)])
148 txts
= [self
.fmt_word
.format(word
)]
149 self
.putg(ss
, es
, Ann
.WORD
, txts
)
150 self
.putpy(ss
, es
, 'WORD', (word
, bus_width
, word_size
))
152 if len(items
) != word_size
:
153 txts
= ['incomplete word size', 'word size', 'ws']
154 self
.putg(ss
, es
, Ann
.WARN
, txts
)
156 self
.word_items
.clear()
158 def queue_word(self
, now
, item
, bus_width
):
159 wordsize
= self
.options
['wordsize']
163 # Terminate a previously seen item of a word first. Emit the
164 # word's annotation when the last item's end was seen.
166 ss
, _
, data
= self
.word_items
[-1]
168 self
.word_items
[-1] = (ss
, es
, data
)
169 if len(self
.word_items
) == wordsize
:
170 self
.flush_word(bus_width
)
172 # Start tracking the currently seen item (yet unknown end time).
174 pend
= (now
, None, item
)
175 self
.word_items
.append(pend
)
177 def handle_bits(self
, now
, item
, bus_width
):
179 # Optionally flush a previously started item.
181 ss
, _
, data
= self
.pend_item
182 self
.pend_item
= None
184 txts
= [self
.fmt_item
.format(data
)]
185 self
.putg(ss
, es
, Ann
.ITEM
, txts
)
186 self
.putpy(ss
, es
, 'ITEM', (data
, bus_width
))
187 self
.putbin(ss
, es
, 0, data
.to_bytes(1, byteorder
='big'))
189 # Optionally queue the currently seen item.
191 self
.pend_item
= (now
, None, item
)
193 # Pass the current item to the word accumulation logic.
194 self
.queue_word(now
, item
, bus_width
)
197 # Determine which (optional) channels have input data. Insist in
198 # a non-empty input data set. Cope with sparse connection maps.
199 # Store enough state to later "compress" sampled input data.
201 idx
if self
.has_channel(idx
) else None
202 for idx
in range(Pin
.DATA_0
, Pin
.DATA_N
)
204 has_data
= [idx
for idx
in data_indices
if idx
is not None]
206 raise ChannelError('Need at least one data channel.')
207 max_connected
= max(has_data
)
209 # Pre-determine which input data to strip off, the width of
210 # individual items and multiplexed words, as well as format
211 # strings here. This simplifies call sites which run in tight
213 upper_data_bound
= max_connected
+ 1
214 num_item_bits
= upper_data_bound
- Pin
.DATA_0
215 num_word_items
= self
.options
['wordsize']
216 num_word_bits
= num_item_bits
* num_word_items
217 num_digits
= (num_item_bits
+ 4 - 1) // 4
218 self
.fmt_item
= "{{:0{}x}}".format(num_digits
)
219 num_digits
= (num_word_bits
+ 4 - 1) // 4
220 self
.fmt_word
= "{{:0{}x}}".format(num_digits
)
222 # Determine .wait() conditions, depending on the presence of a
223 # clock signal. Either inspect samples on the configured edge of
224 # the clock, or inspect samples upon ANY edge of ANY of the pins
225 # which provide input data.
227 cond_idx_clock
= None
228 cond_idx_data_0
= None
229 cond_idx_data_N
= None
230 cond_idx_reset
= None
231 has_clock
= self
.has_channel(Pin
.CLOCK
)
233 cond_idx_clock
= len(conds
)
238 }.get(self
.options
['clock_edge'])
239 conds
.append({Pin
.CLOCK
: edge
})
241 cond_idx_data_0
= len(conds
)
242 conds
.extend([{idx
: 'e'} for idx
in has_data
])
243 cond_idx_data_N
= len(conds
)
244 has_reset
= self
.has_channel(Pin
.RESET
)
246 cond_idx_reset
= len(conds
)
247 conds
.append({Pin
.RESET
: 'e'})
251 }.get(self
.options
['reset_polarity'])
253 # Keep processing the input stream. Assume "always zero" for
254 # not-connected input lines. Pass data bits (all inputs except
255 # clock and reset) to the handle_bits() method. Handle reset
256 # edges first and data changes then, within the same iteration.
257 # This results in robust operation for low-oversampled input.
261 pins
= self
.wait(conds
)
262 except EOFError as e
:
264 clock_edge
= cond_idx_clock
is not None and self
.matched
[cond_idx_clock
]
265 data_edge
= cond_idx_data_0
is not None and [idx
for idx
in range(cond_idx_data_0
, cond_idx_data_N
) if self
.matched
[idx
]]
266 reset_edge
= cond_idx_reset
is not None and self
.matched
[cond_idx_reset
]
269 in_reset
= pins
[Pin
.RESET
] == reset_active
271 self
.handle_bits(self
.samplenum
, None, num_item_bits
)
272 self
.flush_word(num_item_bits
)
276 if clock_edge
or data_edge
:
277 data_bits
= [0 if idx
is None else pins
[idx
] for idx
in data_indices
]
278 data_bits
= data_bits
[:num_item_bits
]
279 item
= bitpack(data_bits
)
280 self
.handle_bits(self
.samplenum
, item
, num_item_bits
)
282 self
.handle_bits(self
.samplenum
, None, num_item_bits
)
283 # TODO Determine whether a WARN annotation needs to get emitted.
284 # The decoder has not seen the end of the last accumulated item.
285 # Instead it just ran out of input data.