def data_ok(self, check, want_len): name = self.state.title() normal, inverted = bitpack(self.data[:8]), bitpack(self.data[8:]) valid = (normal ^ inverted) == 0xff show = inverted if self.state.endswith('#') else normal is_ext_addr = self.is_extended and self.state == 'ADDRESS' if is_ext_addr: normal = bitpack(self.data) show = normal valid = True if len(self.data) == want_len: if self.state == 'ADDRESS': self.addr = normal if self.state == 'COMMAND': self.cmd = normal self.putd(show, want_len) self.ss_start = self.samplenum if is_ext_addr: self.data = [] self.ss_bit = self.ss_start = self.samplenum return True self.putd(show, want_len) if check and not valid: warn_show = bitpack(self.data) self.putx( [Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]]) self.data = [] self.ss_bit = self.ss_start = self.samplenum return valid
def decode(self): chmask = [self.has_channel(i) for i in range(MAX_CHANNELS)] self.num_channels = sum(chmask) if chmask != [i < self.num_channels for i in range(MAX_CHANNELS)]: raise ChannelMapError('Assigned channels need to be contiguous') self.ENCODER_STEPS = 1 << self.num_channels (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait() startbits = (d0, d1, d2, d3, d4, d5, d6, d7) curtime = self.samplenum self.turns.set(self.samplenum, 0) self.count.set(self.samplenum, 0) self.phase.set(self.samplenum, gray_decode(bitpack(startbits[:self.num_channels]))) while True: prevtime = curtime (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait([{i: 'e'} for i in range(self.num_channels)]) bits = (d0, d1, d2, d3, d4, d5, d6, d7) curtime = self.samplenum oldcount = self.count.get() oldphase = self.phase.get() newphase = gray_decode(bitpack(bits[:self.num_channels])) self.phase.set(self.samplenum, newphase) phasedelta_raw = (newphase - oldphase + (self.ENCODER_STEPS // 2 - 1)) % self.ENCODER_STEPS - (self.ENCODER_STEPS // 2 - 1) phasedelta = phasedelta_raw self.increment.set(self.samplenum, phasedelta) if abs(phasedelta) == self.ENCODER_STEPS // 2: phasedelta = 0 self.count.set(self.samplenum, self.count.get() + phasedelta) if self.options['edges']: self.turns.set(self.samplenum, self.count.get() // self.options['edges']) if self.samplerate: period = (curtime - prevtime) / self.samplerate freq = abs(phasedelta_raw) / period self.put(prevtime, curtime, self.out_ann, [4, [ '{}s, {}Hz'.format(prefix_fmt(period), prefix_fmt(freq))]]) if self.options['avg_period']: self.last_n.append((abs(phasedelta_raw), period)) if len(self.last_n) > self.options['avg_period']: self.last_n.popleft() avg_period = sum(v for u, v in self.last_n) / (sum(u for u, v in self.last_n) or 1) self.put(prevtime, curtime, self.out_ann, [5, [ '{}s, {}Hz'.format(prefix_fmt(avg_period), prefix_fmt(1 / avg_period))]]) if self.options['edges']: self.put(prevtime, curtime, self.out_ann, [6, ['{}rpm'.format(prefix_fmt(60 * freq / self.options['edges'], emin=0))]])
def handle_header(self): if len(self.bits) == 1: self.ss_header = self.ss_bit if len(self.bits) == 8: # See tc27xD_um_v2.2.pdf, Figure 20-47, for the header structure bit_len = (self.es_bit - self.ss_header) / 8 value = bitpack(self.bits) ss = self.ss_header es = ss + 3 * bit_len size_id = (value & 0xE0) >> 5 size = payload_sizes.get(size_id) self.payload_size = payload_byte_sizes.get(size_id) self.put_ann(int(ss), int(es), ann_header_pl_size, [size]) ss = es es = ss + 4 * bit_len self.ch_type_id = (value & 0x1E) >> 1 ch_type = channel_types.get(self.ch_type_id) self.put_ann(int(ss), int(es), ann_header_ch_type, [ch_type]) ss = es es = ss + bit_len cts = value & 0x01 self.put_ann(int(ss), int(es), ann_header_cts, ['{}'.format(cts)]) self.bits = [] self.state = state_payload self.timeout = int(9.4 * self.bit_len)
def handle_payload(self): self.timeout = int( (self.payload_size - len(self.payload)) * 8 * self.bit_len) if len(self.bits) == 1: self.ss_byte = self.ss_bit if self.ss_payload == 0: self.ss_payload = self.ss_bit if len(self.bits) == 8: value = bitpack(self.bits) value_hex = '{:02X}'.format(value) # Control transfers have no SIPI payload, show them as control transfers # Check the channel_types list for the meaning of the magic values if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011): self.put_ann(self.ss_byte, self.es_bit, ann_payload, [value_hex]) else: # Control transfers are 8-bit transfers, so only evaluate the first byte if len(self.payload) == 0: ctrl_data = control_payloads.get(value, value_hex) self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [ctrl_data]) else: self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [value_hex]) self.bits = [] self.es_payload = self.es_bit self.payload.append((self.ss_byte, self.es_payload, value)) if (len(self.payload) == self.payload_size): self.timeout = int(1.4 * self.bit_len) self.state = state_sleepbit
def get_data_bits(self, signal): # Save the sample number of the middle of the first data bit. if self.startsample == -1: self.startsample = self.samplenum # Store individual data bits and their start/end samplenumbers. s, halfbit = self.samplenum, int(self.bit_width / 2) self.databits.append([signal, s - halfbit, s + halfbit]) # Return here, unless we already received all data bits. self.cur_data_bit += 1 if self.cur_data_bit < self.options['num_data_bits']: return # Convert accumulated data bits to a data value. bits = [b[0] for b in self.databits] if self.options['bit_order'] == 'msb-first': bits.reverse() self.datavalue = bitpack(bits) b = self.datavalue formatted = self.format_value(b) if formatted is not None: self.putx([0, [formatted]]) self.databits = [] # Advance to either reception of the parity bit, or reception of # the STOP bits if parity is not applicable. self.state = 'GET PARITY BIT' if self.options['parity_type'] == 'none': self.state = 'GET STOP BITS'
def grab_pattern(self, pins): '''Get a bit pattern from potentially incomplete probes' values.''' # Pad and trim the input data, to achieve the user specified # total number of bits. Map all unassigned signals to 0 (low). # Return raw number (unsigned integer interpreation). bits = pins + (None, ) * self.bitcount bits = bits[:self.bitcount] bits = [b if b in (0, 1) else 0 for b in bits] pattern = bitpack(bits) return pattern
def inject_dav_phase(self, ss, es, data): # Inspection of serial input has resulted in one raw byte which # spans a given period of time. Pretend we had seen a DAV active # phase, to re-use code for the parallel transmission. self.ss_raw = ss self.curr_raw = bitpack(data) self.latch_atn = self.curr_atn self.latch_eoi = self.curr_eoi self.es_raw = es self.handle_data_byte() self.ss_raw = self.es_raw = None self.curr_raw = None
def decode_pins(self, pins, idx_strip): bits = [0 if idx is None else pins[idx] for idx in self.idx_channels] item = bitpack(bits[0:idx_strip]) item = (item >> self.left) & self.mask if not self.first: if item != self.last_item: self.put(self.ss_item, self.samplenum, self.out_python, [self.last_item, self.length]) self.ss_item = self.samplenum else: self.ss_item = self.samplenum self.last_item = item self.first = False
def handle_dav_change(self, dav, data): if dav: # Data availability starts when the flag goes active. self.ss_raw = self.samplenum self.curr_raw = bitpack(data) self.latch_atn = self.curr_atn self.latch_eoi = self.curr_eoi return # Data availability ends when the flag goes inactive. Handle the # previously captured data byte according to associated flags. self.es_raw = self.samplenum self.handle_data_byte() self.ss_raw = self.es_raw = None self.curr_raw = None
def decode(self): # Determine which (optional) channels have input data. Insist in # a non-empty input data set. Cope with sparse connection maps. # Store enough state to later "compress" sampled input data. max_possible = len(self.optional_channels) idx_channels = [ idx if self.has_channel(idx) else None for idx in range(max_possible) ] has_channels = [idx for idx in idx_channels if idx is not None] if not has_channels: raise ChannelError('At least one channel has to be supplied.') max_connected = max(has_channels) # Determine .wait() conditions, depending on the presence of a # clock signal. Either inspect samples on the configured edge of # the clock, or inspect samples upon ANY edge of ANY of the pins # which provide input data. if self.has_channel(0): edge = self.options['clock_edge'][0] conds = {0: edge} else: conds = [{idx: 'e'} for idx in has_channels] # Pre-determine which input data to strip off, the width of # individual items and multiplexed words, as well as format # strings here. This simplifies call sites which run in tight # loops later. idx_strip = max_connected + 1 num_item_bits = idx_strip - 1 num_word_items = self.options['wordsize'] num_word_bits = num_item_bits * num_word_items num_digits = (num_item_bits + 3) // 4 self.fmt_item = "{{:0{}x}}".format(num_digits) num_digits = (num_word_bits + 3) // 4 self.fmt_word = "{{:0{}x}}".format(num_digits) # Keep processing the input stream. Assume "always zero" for # not-connected input lines. Pass data bits (all inputs except # clock) to the handle_bits() method. while True: (clk, d0, d1, d2, d3, d4, d5, d6, d7) = self.wait(conds) pins = (clk, d0, d1, d2, d3, d4, d5, d6, d7) bits = [0 if idx is None else pins[idx] for idx in idx_channels] item = bitpack(bits[1:idx_strip]) self.handle_bits(item, num_item_bits)
def handle_sync(self): if len(self.bits) == 1: self.ss_sync = self.ss_bit if len(self.bits) == 16: value = bitpack(self.bits) if value == 0xA84B: self.put_ann(self.ss_sync, self.es_bit, ann_sync, ['Sync OK']) else: self.put_ann(self.ss_sync, self.es_bit, ann_warning, ['Wrong Sync Value: {:02X}'.format(value)]) self.reset() # Only continue if we didn't just reset if self.ss > 0: self.bits = [] self.state = state_header self.timeout = int(9.4 * self.bit_len)
def get_data_bits(self, rxtx, signal): # Save the sample number of the middle of the first data bit. if self.startsample[rxtx] == -1: self.startsample[rxtx] = self.samplenum self.putg([rxtx + 12, ['%d' % signal]]) # Store individual data bits and their start/end samplenumbers. s, halfbit = self.samplenum, int(self.bit_width / 2) self.databits[rxtx].append([signal, s - halfbit, s + halfbit]) # Return here, unless we already received all data bits. self.cur_data_bit[rxtx] += 1 if self.cur_data_bit[rxtx] < self.options['data_bits']: return # Convert accumulated data bits to a data value. bits = [b[0] for b in self.databits[rxtx]] if self.options['bit_order'] == 'msb-first': bits.reverse() self.datavalue[rxtx] = bitpack(bits) self.putpx(rxtx, ['DATA', rxtx, (self.datavalue[rxtx], self.databits[rxtx])]) b = self.datavalue[rxtx] formatted = self.format_value(b) if formatted is not None: self.putx(rxtx, [rxtx, [formatted]]) bdata = b.to_bytes(self.bw, byteorder='big') self.putbin(rxtx, [rxtx, bdata]) self.putbin(rxtx, [2, bdata]) self.handle_packet(rxtx) self.databits[rxtx] = [] # Advance to either reception of the parity bit, or reception of # the STOP bits if parity is not applicable. self.state[rxtx] = 'GET PARITY BIT' if self.options['parity'] == 'none': self.state[rxtx] = 'GET STOP BITS'
def frame_flush(self): syms = self.symbols_clear() while syms and syms[0][2] == 'IDLE': syms.pop(0) while syms and syms[-1][2] == 'IDLE': syms.pop(-1) if not syms: return text = [] data = [] for sym in syms: if sym[2] == 'FRAME_INIT': text.append('INIT') data.append('INIT') continue if sym[2] == 'SYNC_PAD': if not text or text[-1] != 'SYNC': text.append('SYNC') data.append('SYNC') continue if sym[2] == 'DATA_BYTE': b = [bit[3] for bit in sym[3] if bit[2] == 'DATA_BIT'] b = bitpack(b) text.append('{:02x}'.format(b)) data.append(b) continue if sym[2] == 'SHORT_BIT': if not text or text[-1] != 'SHORT': text.append('SHORT') data.append('SHORT') continue if sym[2] == 'WAIT_ACK': text.append('WAIT') data.append('WAIT') continue text = ' '.join(text) ss, es = syms[0][0], syms[-1][1] self.putg(ss, es, [ANN_FRAME_BYTES, [text]]) self.putpy(ss, es, 'FRAME_DATA', data)
def get_data_bits(self, rxtx, signal): # Save the sample number of the middle of the first data bit. if self.startsample[rxtx] == -1: self.startsample[rxtx] = self.samplenum self.putg([Ann.RX_DATA_BIT + rxtx, ['%d' % signal]]) # Store individual data bits and their start/end samplenumbers. s, halfbit = self.samplenum, int(self.bit_width / 2) self.databits[rxtx].append([signal, s - halfbit, s + halfbit]) self.cur_frame_bit[rxtx] += 1 # Return here, unless we already received all data bits. self.cur_data_bit[rxtx] += 1 if self.cur_data_bit[rxtx] < self.options['data_bits']: return # Convert accumulated data bits to a data value. bits = [b[0] for b in self.databits[rxtx]] if self.options['bit_order'] == 'msb-first': bits.reverse() self.datavalue[rxtx] = bitpack(bits) self.putpx(rxtx, ['DATA', rxtx, (self.datavalue[rxtx], self.databits[rxtx])]) b = self.datavalue[rxtx] formatted = self.format_value(b) if formatted is not None: self.putx(rxtx, [rxtx, [formatted]]) bdata = b.to_bytes(self.bw, byteorder='big') self.putbin(rxtx, [Bin.RX + rxtx, bdata]) self.putbin(rxtx, [Bin.RXTX, bdata]) self.handle_packet(rxtx) self.databits[rxtx] = [] self.advance_state(rxtx, signal)
def decode(self): if not self.samplerate or self.samplerate < 1e6: raise SamplerateError('Need a samplerate of at least 1MSa/s') # As a special case the first low period in the input capture is # saught regardless of whether we can see its falling edge. This # approach is also used to recover after synchronization was lost. # # The important condition here in the main loop is: Get the next # edge's position, but time out after a maximum period of four # data bits. This allows for the detection of SYNC pulses, also # responds "soon enough" to DATA bits where edges can be few # within a data byte. Also avoids excessive waits for unexpected # communication errors. # # DATA bits within a byte are taken at fixed intervals relative # to the SYNC-PAD's falling edge. It's essential to check the # carrier at every edge, also during DATA bit sampling. Simple # skips to the desired sample point could break that feature. while True: # Help kick-start the IDLE condition detection after # decoder state reset. if not self.edges: curr_level, = self.wait({PIN_DATA: 'l'}) self.carrier_check(curr_level, self.samplenum) self.edges = [self.samplenum] continue # Advance to the next edge, or over a medium span without an # edge. Prepare to classify the distance to derive bit types # from these details. last_snum = self.samplenum curr_level, = self.wait([{ PIN_DATA: 'e' }, { 'skip': self.lookahead_width }]) self.carrier_check(curr_level, self.samplenum) bit_level = curr_level edge_seen = self.matched[0] if edge_seen: bit_level = 1 - bit_level if not self.edges: self.edges = [self.samplenum] continue self.edges.append(self.samplenum) curr_snum = self.samplenum # Check bit width (can also be multiple data bits). span = self.edges[-1] - self.edges[-2] is_pad = bit_level and self.span_is_pad(span) is_data = self.span_is_data(span) is_short = bit_level and self.span_is_short(span) if is_pad: # BEWARE! Use ss value of last edge (genuinely seen, or # inserted after a DATA byte) for PAD bit annotations. ss, es = self.edges[-2], curr_snum texts = ['PAD', '{:d}'.format(bit_level)] self.putg(ss, es, [ANN_PAD_BIT, texts]) self.symbols_append(ss, es, 'PAD_BIT', bit_level) ss, es = self.symbols_get_last()[:2] self.putpy(ss, es, 'PAD_BIT', bit_level) continue if is_short: ss, es = last_snum, curr_snum texts = ['SHORT', '{:d}'.format(bit_level)] self.putg(ss, es, [ANN_SHORT_DATA, texts]) self.symbols_append(ss, es, 'SHORT_BIT', bit_level) ss, es = self.symbols_get_last()[:2] self.putpy(ss, es, 'SHORT_BIT', bit_level) continue # Force IDLE period check when the decoder seeks to sync # to the input data stream. if not bit_level and not self.symbols and self.carrier_want_idle: continue # Accept arbitrary length LOW phases after DATA bytes(!) or # SHORT pulses, but not within a DATA byte or SYNC-PAD etc. # This covers the late start of the next SYNC-PAD (byte of # a frame, or ACK byte after a frame, or the start of the # next frame). if not bit_level: if self.symbols_has_prev('DATA_BYTE'): continue if self.symbols_has_prev('SHORT_BIT'): continue if self.symbols_has_prev('WAIT_ACK'): continue # Get (consume!) the LOW DATA bit after a PAD. took_low = False if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'): took_low = True is_data -= 1 next_snum = int(last_snum + self.data_width) ss, es = last_snum, next_snum texts = ['ZERO', '{:d}'.format(bit_level)] self.putg(ss, es, [ANN_LOW_BIT, texts]) self.symbols_append(ss, es, 'ZERO_BIT', bit_level) ss, es = self.symbols_get_last()[:2] self.putpy(ss, es, 'DATA_BIT', bit_level) self.data_fall_time = last_snum last_snum = next_snum # Turn the combination of PAD and LOW DATA into SYNC-PAD. # Start data bit accumulation after a SYNC-PAD was seen. sync_pad_seq = ['PAD_BIT', 'ZERO_BIT'] if self.symbols_has_prev(sync_pad_seq): self.symbols_collapse(len(sync_pad_seq), 'SYNC_PAD') ss, es = self.symbols_get_last()[:2] self.putpy(ss, es, 'SYNC_PAD', True) self.data_bits = [] # Turn three subsequent SYNC-PAD into FRAME-INIT. Start the # accumulation of frame bytes when FRAME-INIT was seen. frame_init_seq = 3 * ['SYNC_PAD'] if self.symbols_has_prev(frame_init_seq): self.symbols_collapse(len(frame_init_seq), 'FRAME_INIT') # Force a flush of the previous frame after we have # reliably detected the start of another one. This is a # workaround for this decoder's inability to detect the # end of a frame after an ACK was seen or byte counts # have been reached. We cannot assume perfect input, # thus we leave all interpretation of frame content to # upper layers. Do keep the recently queued FRAME_INIT # symbol across the flush operation. if len(self.symbols) > 1: keep = self.symbols.pop(-1) self.frame_flush() self.symbols.clear() self.symbols.append(keep) ss, es = self.symbols_get_last()[:2] texts = ['FRAME INIT', 'INIT', 'I'] self.putg(ss, es, [ANN_FRAME_INIT, texts]) self.putpy(ss, es, 'FRAME_INIT', True) self.frame_bytes = [] # Collapse SYNC-PAD after SHORT+ into a WAIT-ACK. Include # all leading SHORT bits in the WAIT as well. wait_ack_seq = ['SHORT_BIT', 'SYNC_PAD'] if self.symbols_has_prev(wait_ack_seq): self.symbols_collapse(len(wait_ack_seq), 'WAIT_ACK', squeeze='SHORT_BIT') ss, es = self.symbols_get_last()[:2] texts = [ 'WAIT for sync response', 'WAIT response', 'WAIT', 'W' ] self.putg(ss, es, [ANN_FRAME_WAIT, texts]) self.putpy(ss, es, 'SYNC_RESP_WAIT', True) if took_low and not is_data: # Start at the very next edge if we just consumed a LOW # after a PAD bit, and the DATA bit count is exhausted. # This improves robustness, deals with inaccurate edge # positions. (Motivated by real world captures, the spec # would not discuss bit time tolerances.) continue # When we get here, the only remaining (the only supported) # activity is the collection of a data byte's DATA bits. # These are not taken by the main loop's "edge search, with # a timeout" approach, which is "too tolerant". Instead all # DATA bits get sampled at a fixed interval and relative to # the SYNC-PAD's falling edge. We expect to have seen the # data byte' SYNC-PAD before. If we haven't, the decoder is # not yet synchronized to the input data. if not is_data: fast_cont = edge_seen and curr_level ss, es = last_snum, curr_snum texts = ['failed pulse length check', 'pulse length', 'length'] self.putg(ss, es, [ANN_SYNC_LOSS, texts]) self.frame_flush() self.carrier_flush() self.reset_state() if fast_cont: self.edges = [self.samplenum] continue if not self.symbols_has_prev('SYNC_PAD'): # Fast reponse to the specific combination of: no-sync, # edge seen, and current high level. In this case we # can reset internal state, but also can continue the # interpretation right after the most recently seen # rising edge, which could start the next PAD time. # Otherwise continue slow interpretation after reset. fast_cont = edge_seen and curr_level self.frame_flush() self.carrier_flush() self.reset_state() if fast_cont: self.edges = [self.samplenum] continue # The main loop's "edge search with period timeout" approach # can have provided up to three more DATA bits after the LOW # bit of the SYNC-PAD. Consume them immediately in that case, # otherwise .wait() for their sample point. Stick with float # values for bit sample points and bit time boundaries for # improved accuracy, only round late to integers when needed. bit_field = [] bit_ss = self.data_fall_time + self.data_width for bit_idx in range(8): bit_es = bit_ss + self.data_width bit_snum = (bit_es + bit_ss) / 2 if bit_snum > self.samplenum: bit_level, = self.wait_until(bit_snum) ss, es = ceil(bit_ss), floor(bit_es) texts = ['{:d}'.format(bit_level)] self.putg(ss, es, [ANN_DATA_BIT, texts]) self.symbols_append(ss, es, 'DATA_BIT', bit_level) ss, es = self.symbols_get_last()[:2] self.putpy(ss, es, 'DATA_BIT', bit_level) bit_field.append(bit_level) if self.data_bits is not None: self.data_bits.append(bit_level) bit_ss = bit_es end_snum = bit_es curr_level, = self.wait_until(end_snum) curr_snum = self.samplenum # We are at the exact _calculated_ boundary of the last DATA # bit time. Improve robustness for those situations where # the transmitter's and the sender's timings differ within a # margin, and the transmitter may hold the last DATA bit's # HIGH level for a little longer. # # When no falling edge is seen within the maximum tolerance # for the last DATA bit, then this could be the combination # of a HIGH DATA bit and a PAD bit without a LOW in between. # Fake an edge in that case, to re-use existing code paths. # Make sure to keep referencing times to the last SYNC pad's # falling edge. This is the last reliable condition we have. if curr_level: hold = self.hold_high_width curr_level, = self.wait([{PIN_DATA: 'l'}, {'skip': int(hold)}]) self.carrier_check(curr_level, self.samplenum) if self.matched[1]: self.edges.append(curr_snum) curr_level = 1 - curr_level curr_snum = self.samplenum # Get the byte value from the bits (when available). # TODO Has the local 'bit_field' become obsolete, or should # self.data_bits go away? data_byte = bitpack(bit_field) if self.data_bits is not None: data_byte = bitpack(self.data_bits) self.data_bits.clear() if self.frame_bytes is not None: self.frame_bytes.append(data_byte) # Turn a sequence of a SYNC-PAD and eight DATA bits into a # DATA-BYTE symbol. byte_seq = ['SYNC_PAD'] + 8 * ['DATA_BIT'] if self.symbols_has_prev(byte_seq): self.symbols_collapse(len(byte_seq), 'DATA_BYTE') ss, es = self.symbols_get_last()[:2] texts = ['{:02x}'.format(data_byte)] self.putg(ss, es, [ANN_DATA_BYTE, texts]) self.putpy(ss, es, 'DATA_BYTE', data_byte) # Optionally terminate the accumulation of a frame when a # WAIT-ACK period was followed by a DATA-BYTE? This could # flush the current packet before the next FRAME-INIT or # IDLE are seen, and increases usability for short input # data (aggressive trimming). It won't help when WAIT is # not seen, though. sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE'] if self.symbols_has_prev(sync_resp_seq): self.frame_flush()
def decode(self): # Determine which (optional) channels have input data. Insist in # a non-empty input data set. Cope with sparse connection maps. # Store enough state to later "compress" sampled input data. data_indices = [ idx if self.has_channel(idx) else None for idx in range(Pin.DATA_0, Pin.DATA_N) ] has_data = [idx for idx in data_indices if idx is not None] if not has_data: raise ChannelError('Need at least one data channel.') max_connected = max(has_data) # Pre-determine which input data to strip off, the width of # individual items and multiplexed words, as well as format # strings here. This simplifies call sites which run in tight # loops later. upper_data_bound = max_connected + 1 num_item_bits = upper_data_bound - Pin.DATA_0 num_word_items = self.options['wordsize'] num_word_bits = num_item_bits * num_word_items num_digits = (num_item_bits + 4 - 1) // 4 self.fmt_item = "{{:0{}x}}".format(num_digits) num_digits = (num_word_bits + 4 - 1) // 4 self.fmt_word = "{{:0{}x}}".format(num_digits) # Determine .wait() conditions, depending on the presence of a # clock signal. Either inspect samples on the configured edge of # the clock, or inspect samples upon ANY edge of ANY of the pins # which provide input data. conds = [] cond_idx_clock = None cond_idx_data_0 = None cond_idx_data_N = None cond_idx_reset = None has_clock = self.has_channel(Pin.CLOCK) if has_clock: cond_idx_clock = len(conds) edge = { 'rising': 'r', 'falling': 'f', 'either': 'e', }.get(self.options['clock_edge']) conds.append({Pin.CLOCK: edge}) else: cond_idx_data_0 = len(conds) conds.extend([{idx: 'e'} for idx in has_data]) cond_idx_data_N = len(conds) has_reset = self.has_channel(Pin.RESET) if has_reset: cond_idx_reset = len(conds) conds.append({Pin.RESET: 'e'}) reset_active = { 'low-active': 0, 'high-active': 1, }.get(self.options['reset_polarity']) # Keep processing the input stream. Assume "always zero" for # not-connected input lines. Pass data bits (all inputs except # clock and reset) to the handle_bits() method. Handle reset # edges first and data changes then, within the same iteration. # This results in robust operation for low-oversampled input. in_reset = False while True: pins = self.wait(conds) clock_edge = cond_idx_clock is not None and self.matched[ cond_idx_clock] 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] ] reset_edge = cond_idx_reset is not None and self.matched[ cond_idx_reset] if reset_edge: in_reset = pins[Pin.RESET] == reset_active if in_reset: self.handle_bits(self.samplenum, None, num_item_bits) self.flush_word(num_item_bits) if in_reset: continue if clock_edge or data_edge: data_bits = [ 0 if idx is None else pins[idx] for idx in data_indices ] data_bits = data_bits[:num_item_bits] item = bitpack(data_bits) self.handle_bits(self.samplenum, item, num_item_bits)
def decode(self): last_sent = None timeout_ms = self.options['timeout_ms'] want_unit = self.options['unit'] show_all = self.options['changes'] == 'no' wait_cond = [{0: 'r'}] if timeout_ms: snum_per_ms = self.samplerate / 1000 timeout_snum = timeout_ms * snum_per_ms wait_cond.append({'skip': round(timeout_snum)}) while True: # Sample data at the rising clock edge. Optionally timeout # after inactivity for a user specified period. Present the # number of unprocessed bits to the user for diagnostics. clk, data = self.wait(wait_cond) if timeout_ms and not self.matched[0]: if self.number_bits or self.flags_bits: count = len(self.number_bits) + len(self.flags_bits) self.putg(self.ss, self.samplenum, 1, [ 'timeout with {} bits in buffer'.format(count), 'timeout ({} bits)'.format(count), 'timeout', ]) self.reset() continue # Store position of first bit and last activity. # Shift in measured number and flag bits. if not self.ss: self.ss = self.samplenum self.es = self.samplenum if len(self.number_bits) < 16: self.number_bits.append(data) continue if len(self.flags_bits) < 8: self.flags_bits.append(data) if len(self.flags_bits) < 8: continue # Get raw values from received data bits. Run the number # conversion, controlled by flags and/or user specs. negative = bool(self.flags_bits[4]) is_inch = bool(self.flags_bits[7]) number = bitpack(self.number_bits) if negative: number = -number if is_inch: number /= 2000 if want_unit == 'mm': number *= mm_per_inch is_inch = False else: number /= 100 if want_unit == 'inch': number = round(number / mm_per_inch, 4) is_inch = True unit = 'in' if is_inch else 'mm' # Construct and emit an annotation. if show_all or (number, unit) != last_sent: self.putg(self.ss, self.es, 0, [ '{number}{unit}'.format(**locals()), '{number}'.format(**locals()), ]) last_sent = (number, unit) # Reset internal state for the start of the next packet. self.reset()
def decode(self): # Wait for samples if not self.samplerate: raise SamplerateError('No input data') # Polarity config auto (sample 0 as reference) if self.options['polarity'] == 'auto': level, = self.wait({'skip': 0}) active = 1 - level # Polarity config manual else: active = 0 if self.options['polarity'] == 'active low' else 1 # Main while True: # Wait for pin change self.ri, = self.wait({Pin.RI: 'e'}) # Pin active if self.ri != active: if self.state != 'Stop': continue # Long idle reset space = self.samplenum - self.samplenum_save if self.state != 'Stop' and space > self.idle: self.reset() # Start bit if self.state == 'Idle': if self.tolerance(space, self.header): self.put(self.samplenum_field, self.samplenum, self.anchor, [Ann.BitStart, ['Start', 'S']]) self.put(self.samplenum_field, self.samplenum, self.anchor, [Ann.Header, ['Start', 'S']]) self.samplenum_decode = self.samplenum_field self.data = [] self.state = 'Code' self.samplenum_save = self.samplenum_field = self.samplenum # Code bits elif self.state == 'Code': bit = None if self.tolerance(space, self.lo): bit = 0 elif self.tolerance(space, self.hi): bit = 1 if bit in (0, 1): self.put(self.samplenum_save, self.samplenum, self.anchor, [Ann.BitCode, ['{:d}'.format(bit)]]) self.data.insert(0, bit) else: self.reset() self.samplenum_save = self.samplenum # Code complete if len(self.data) == 12: code = bitpack(self.data) f = '0x{{:0{}X}}'.format(3) self.put(self.samplenum_field, self.samplenum, self.anchor, [Ann.Code, [f.format(code)]]) self.hex = code self.samplenum_field = self.samplenum self.state = 'Stop' # Stop elif self.state == 'Stop': self.put(self.samplenum_field, self.samplenum_save + self.stop, self.anchor, [Ann.BitStop, ['Stop', 'S']]) self.put(self.samplenum_field, self.samplenum_save + self.stop, self.anchor, [Ann.Footer, ['Stop', 'S']]) oricode = ori.get(self.hex, 'Unknown RI code') self.put(self.samplenum_decode, self.samplenum_save + self.stop, self.anchor, [Ann.Decode, [ '{}'.format(oricode), ]]) self.samplenum_save = self.samplenum_field = self.samplenum self.state = 'Idle'