diff --git a/decoders/can/pd.py b/decoders/can/pd.py index fcd13e6a..f27ffd72 100644 --- a/decoders/can/pd.py +++ b/decoders/can/pd.py @@ -2,7 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2013 Uwe Hermann -## Copyright (C) 2019 Stephan Thiele +## Copyright (C) 2019-2026 Stephan Thiele ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -27,6 +27,16 @@ class SamplerateError(Exception): def dlc2len(dlc): return [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64][dlc] +def gray2num(gray_code): + num = gray_code + mask = gray_code + + while mask: + mask >>= 1 + num ^= mask + + return num + class Decoder(srd.Decoder): api_version = 3 id = 'can' @@ -64,10 +74,11 @@ class Decoder(srd.Decoder): ('stuff-bit', 'Stuff bit'), ('warning', 'Warning'), ('bit', 'Bit'), + ('sbc', 'Stuff bit count') ) annotation_rows = ( ('bits', 'Bits', (15, 17)), - ('fields', 'Fields', tuple(range(15))), + ('fields', 'Fields', tuple(range(15)) + (18,)), ('warnings', 'Warnings', (16,)), ) @@ -92,6 +103,17 @@ def set_nominal_bitrate(self): def set_fast_bitrate(self): self.set_bit_rate(self.options['fast_bitrate']) + def set_dlc_and_crc_len(self, dlc): + self.dlc = dlc + + if self.fd: + if dlc2len(self.dlc) < 16: + self.crc_len = 17 + else: + self.crc_len = 21 + else: + self.crc_len = 15 + def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value @@ -129,6 +151,7 @@ def reset_variables(self): self.bits = [] # Only actual CAN frame bits (no stuff bits) self.curbit = 0 # Current bit of CAN frame (bit 0 == SOF) self.last_databit = 999 # Positive value that bitnum+x will never match + self.crc_start = 999 self.ss_block = None self.ss_bit12 = None self.ss_bit32 = None @@ -137,6 +160,8 @@ def reset_variables(self): self.rtr_type = None self.fd = False self.rtr = None + self.last_bit_was_stuff_bit = False + self.crc_len = 15 # Poor man's clock synchronization. Use signal edges which change to # dominant state in rather simple ways. This naive approach is neither @@ -155,19 +180,55 @@ def get_sample_point(self, bitnum): return int(samplenum) def is_stuff_bit(self): - # CAN uses NRZ encoding and bit stuffing. - # After 5 identical bits, a stuff bit of opposite value is added. - # But not in the CRC delimiter, ACK, and end of frame fields. - if len(self.bits) > self.last_databit + 17: + # CAN uses NRZ encoding (dynamic bit stuffing). + # After five consecutive bits of identical value, a stuff bit of the + # opposite polarity is inserted. + # + # In CAN-FD frames, additional fixed bit stuffing is used for the CRC field. + # This fixed stuffing begins one bit before the CRC field, regardless of + # whether five identical bits have occurred. + + # If a dynamic stuff bit and a fixed stuff bit would be inserted at the + # same position, only the fixed stuff bit is inserted. In this case, + # only a single stuff bit is inserted: + if self.last_bit_was_stuff_bit: + self.last_bit_was_stuff_bit = False return False - last_6_bits = self.rawbits[-6:] - if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]): + + cur_bit = len(self.bits) - 1 + + # Bit stuffing is not applied to the CRC delimiter, ACK field, + # or End-of-Frame (EOF) field: + if cur_bit > self.crc_start + self.crc_len - 1: + self.last_bit_was_stuff_bit = False return False + if self.fd and cur_bit > self.last_databit: + # Within the CAN-FD CRC field, a fixed stuff bit is inserted after every fourth bit: + if (cur_bit - self.last_databit - 1) % 4 != 0: + self.last_bit_was_stuff_bit = False + return False + else: + # NRZ dynamic bit stuffing: + last_6_bits = self.rawbits[-6:] + if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]): + self.last_bit_was_stuff_bit = False + return False + # Stuff bit. Keep it in self.rawbits, but drop it from self.bits. self.bits.pop() # Drop last bit. + self.last_bit_was_stuff_bit = True return True + def is_valid_parity(self, num, given_parity_bit): + calculated_parity_bit = 0 + + while num: + calculated_parity_bit ^= num & 1 + num >>= 1 + + return calculated_parity_bit == given_parity_bit + def is_valid_crc(self, crc_bits): return True # TODO @@ -182,29 +243,36 @@ def decode_overload_frame(self, bits): # Returns True if the frame ended (EOF), False otherwise. def decode_frame_end(self, can_rx, bitnum): - # Remember start of CRC sequence (see below). + # Remember start of Non-FD CRC sequence or FD-SBC field (see below). if bitnum == (self.last_databit + 1): self.ss_block = self.samplenum - if self.fd: - if dlc2len(self.dlc) < 16: - self.crc_len = 27 # 17 + SBC + stuff bits - else: - self.crc_len = 32 # 21 + SBC + stuff bits - else: - self.crc_len = 15 + elif self.fd and bitnum == (self.last_databit + 4): + # SBC field + x = self.last_databit + 1 + sbc_bits = self.bits[x:x + self.last_databit + 4] + p_gray_sbc = bitpack_msb(sbc_bits) - # CRC sequence (15 bits, 17 bits or 21 bits) - elif bitnum == (self.last_databit + self.crc_len): - if self.fd: - if dlc2len(self.dlc) < 16: - crc_type = "CRC-17" - else: - crc_type = "CRC-21" - else: - crc_type = "CRC-15" + parity = p_gray_sbc & 1 + gray_sbc = p_gray_sbc >> 1 - x = self.last_databit + 1 + if not self.is_valid_parity(gray_sbc, parity): + self.putb([16, ['Parity is invalid']]) + + sbc = gray2num(gray_sbc) # Number of stuff bits modulo 8 + + self.putb([18, ['Stuff bit count: %d' % sbc, + 'SBC: %d' % sbc, 'SBC']]) + + # Remember start of FD-CRC sequence (see below). + elif self.fd and bitnum == (self.last_databit + 4 + 1): + self.ss_block = self.samplenum + + # CRC sequence (15 bits, 17 bits or 21 bits) + elif bitnum == (self.crc_start - 1 + self.crc_len): + x = self.crc_start crc_bits = self.bits[x:x + self.crc_len + 1] + + crc_type = "CRC-%d" % self.crc_len self.crc = bitpack_msb(crc_bits) self.putb([11, ['%s sequence: 0x%04x' % (crc_type, self.crc), '%s: 0x%04x' % (crc_type, self.crc), '%s' % crc_type]]) @@ -212,7 +280,7 @@ def decode_frame_end(self, can_rx, bitnum): self.putb([16, ['CRC is invalid']]) # CRC delimiter bit (recessive) - elif bitnum == (self.last_databit + self.crc_len + 1): + elif bitnum == (self.crc_start - 1 + self.crc_len + 1): self.putx([12, ['CRC delimiter: %d' % can_rx, 'CRC d: %d' % can_rx, 'CRC d']]) if can_rx != 1: @@ -222,23 +290,23 @@ def decode_frame_end(self, can_rx, bitnum): self.set_nominal_bitrate() # ACK slot bit (dominant: ACK, recessive: NACK) - elif bitnum == (self.last_databit + self.crc_len + 2): + elif bitnum == (self.crc_start - 1 + self.crc_len + 2): ack = 'ACK' if can_rx == 0 else 'NACK' self.putx([13, ['ACK slot: %s' % ack, 'ACK s: %s' % ack, 'ACK s']]) # ACK delimiter bit (recessive) - elif bitnum == (self.last_databit + self.crc_len + 3): + elif bitnum == (self.crc_start - 1 + self.crc_len + 3): self.putx([14, ['ACK delimiter: %d' % can_rx, 'ACK d: %d' % can_rx, 'ACK d']]) if can_rx != 1: self.putx([16, ['ACK delimiter must be a recessive bit']]) # Remember start of EOF (see below). - elif bitnum == (self.last_databit + self.crc_len + 4): + elif bitnum == (self.crc_start - 1 + self.crc_len + 4): self.ss_block = self.samplenum # End of frame (EOF), 7 recessive bits - elif bitnum == (self.last_databit + self.crc_len + 10): + elif bitnum == (self.crc_start - 1 + self.crc_len + 3 + 7): self.putb([2, ['End of frame', 'EOF', 'E']]) if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]: self.putb([16, ['End of frame (EOF) must be 7 recessive bits']]) @@ -295,10 +363,15 @@ def decode_standard_frame(self, can_rx, bitnum): # Bits 15-18: Data length code (DLC), in number of bytes (0-8). elif bitnum == self.dlc_start + 3: - self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4]) + self.set_dlc_and_crc_len(bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])) self.putb([10, ['Data length code: %d' % self.dlc, 'DLC: %d' % self.dlc, 'DLC']]) self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8) + self.crc_start = self.last_databit + 1 + + if self.fd: + self.crc_start += 4 # Skip SBC field + if self.dlc > 8 and not self.fd: self.putb([16, ['Data length code (DLC) > 8 is not allowed']]) @@ -397,10 +470,14 @@ def decode_extended_frame(self, can_rx, bitnum): # Bits 35-38: Data length code (DLC), in number of bytes (0-8). elif bitnum == self.dlc_start + 3: - self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4]) + self.set_dlc_and_crc_len(bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])) self.putb([10, ['Data length code: %d' % self.dlc, 'DLC: %d' % self.dlc, 'DLC']]) self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8) + self.crc_start = self.last_databit + 1 + + if self.fd: + self.crc_start += 4 # Skip SBC field # Remember all databyte bits, except the very last one. elif bitnum in range(self.dlc_start + 4, self.last_databit):