diff --git a/decoders/can/__init__.py b/decoders/can/__init__.py index 888bb819..b9973d09 100644 --- a/decoders/can/__init__.py +++ b/decoders/can/__init__.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012 Uwe Hermann +## 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 @@ -25,7 +26,7 @@ the digital output side of a CAN transceiver IC such as the Microchip MCP-2515DM-BM). -It also has support for CAN-FD. +It also has support for CAN-FD and CAN-XL. ''' from .pd import Decoder diff --git a/decoders/can/pd.py b/decoders/can/pd.py index fcd13e6a..a8c5a58e 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' @@ -42,8 +52,11 @@ class Decoder(srd.Decoder): ) options = ( {'id': 'nominal_bitrate', 'desc': 'Nominal bitrate (bits/s)', 'default': 1000000}, - {'id': 'fast_bitrate', 'desc': 'Fast bitrate (bits/s)', 'default': 2000000}, - {'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0}, + {'id': 'fd_bitrate', 'desc': 'FD bitrate (bits/s)', 'default': 2000000}, + {'id': 'xl_bitrate', 'desc': 'XL bitrate (bits/s)', 'default': 4000000}, + {'id': 'nominal_sample_point', 'desc': 'Nominal sample point (%)', 'default': 70.0}, + {'id': 'fd_sample_point', 'desc': 'FD sample point (%)', 'default': 70.0}, + {'id': 'xl_sample_point', 'desc': 'XL sample point (%)', 'default': 70.0}, ) annotations = ( ('data', 'Payload data'), @@ -64,10 +77,27 @@ class Decoder(srd.Decoder): ('stuff-bit', 'Stuff bit'), ('warning', 'Warning'), ('bit', 'Bit'), + ('sbc', 'Stuff bit count'), + ('xlf', 'Extended data length format'), + ('resXL', 'Reserved bit extended data field length format'), + ('ADH', 'Arbitration to data high'), + ('DH1', 'Data high bit 1'), + ('DH2', 'Data high bit 2'), + ('DL1', 'Data low bit 1'), + ('SDT', 'Service data unit type'), + ('SEC', 'Simple/extended content'), + ('PCRC-13', 'Preface CRC-13'), + ('VCID', 'Virtual CAN network channel identifier'), + ('AF', 'Acceptance field'), + ('FCP', 'Format check pattern'), + ('DAH', 'Data arbitration high'), + ('AH1', 'Arbitration high 1'), + ('AL1', 'Arbitration low 1'), + ('AH2', 'Arbitration high 2') ) annotation_rows = ( ('bits', 'Bits', (15, 17)), - ('fields', 'Fields', tuple(range(15))), + ('fields', 'Fields', tuple(range(15)) + (18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34)), ('warnings', 'Warnings', (16,)), ) @@ -82,27 +112,67 @@ def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.out_python = self.register(srd.OUTPUT_PYTHON) - def set_bit_rate(self, bitrate): + def set_bit_rate(self, bitrate, sample_point): + prev_bit_remaining_samples = self.bit_width - self.sample_point + self.bit_width = float(self.samplerate) / float(bitrate) - self.sample_point = (self.bit_width / 100.0) * self.options['sample_point'] + self.sample_point = (self.bit_width / 100.0) * sample_point + + if self.fd: + # Set virtual dom edge after the bit where bitrate switch happened, to adjust sample grid to new bitrate + if self.xl: + self.dom_edge_bcount = self.curbit + self.dom_edge_snum = self.samplenum + prev_bit_remaining_samples + else: + self.dom_edge_bcount = self.curbit + 1 + self.dom_edge_snum = self.samplenum + self.bit_width - self.sample_point def set_nominal_bitrate(self): - self.set_bit_rate(self.options['nominal_bitrate']) + self.set_bit_rate(self.options['nominal_bitrate'], self.options['nominal_sample_point']) + + def set_fd_bitrate(self): + self.set_bit_rate(self.options['fd_bitrate'], self.options['fd_sample_point']) - def set_fast_bitrate(self): - self.set_bit_rate(self.options['fast_bitrate']) + def set_xl_bitrate(self): + self.set_bit_rate(self.options['xl_bitrate'], self.options['xl_sample_point']) + + def set_dlc_and_crc_len(self, dlc): + self.dlc = dlc + + if self.xl: + self.crc_len = 32 + elif 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 self.bit_width = float(self.samplerate) / float(self.options['nominal_bitrate']) - self.sample_point = (self.bit_width / 100.0) * self.options['sample_point'] + self.sample_point = (self.bit_width / 100.0) * self.options['nominal_sample_point'] # Generic helper for CAN bit annotations. def putg(self, ss, es, data): left, right = int(self.sample_point), int(self.bit_width - self.sample_point) self.put(ss - left, es + right, self.out_ann, data) + # Single-CAN-bit annotation for a bitrate switch bit using the current samplenum. + def putx_brs(self, from_bitrate, from_spp, to_bitrate, to_spp, data): + from_samples_per_bit = float(self.samplerate) / float(from_bitrate) + from_spp_sample_no = (from_samples_per_bit / 100.0) * from_spp + + to_samples_per_bit = float(self.samplerate) / float(to_bitrate) + to_spp_sample_no = (to_samples_per_bit / 100.0) * to_spp + + num_samples_brs_bit = from_spp_sample_no + to_samples_per_bit - to_spp_sample_no + + left, right = int(from_spp_sample_no), int(num_samples_brs_bit - from_spp_sample_no) + self.put(self.samplenum - left, self.samplenum + right, self.out_ann, data) + # Single-CAN-bit annotation using the current samplenum. def putx(self, data): self.putg(self.samplenum, self.samplenum, data) @@ -129,6 +199,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 @@ -136,7 +207,12 @@ def reset_variables(self): self.frame_bytes = [] self.rtr_type = None self.fd = False + self.xl = False + self.brs = False self.rtr = None + self.last_bit_was_stuff_bit = False + self.crc_len = 15 + self.dlc_field_len = 4 # Poor man's clock synchronization. Use signal edges which change to # dominant state in rather simple ways. This naive approach is neither @@ -155,19 +231,65 @@ 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.xl and cur_bit > 21: # After DL1 bit + # Within the CAN-XL Control field (starting from DL1 bit), data field and FCRC sequence, a fixed stuff bit + # is inserted after every 10th bit: + if (cur_bit - 20) % 10 != 0: + self.last_bit_was_stuff_bit = False + return False + + elif not self.xl and 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_even_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_odd_parity(self, num, given_parity_bit): + return not self.is_valid_even_parity(num, given_parity_bit) + def is_valid_crc(self, crc_bits): return True # TODO @@ -182,63 +304,105 @@ 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, XL FCRC 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 not self.xl 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_even_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 not self.xl and bitnum == (self.last_databit + 4 + 1): + self.ss_block = self.samplenum + + # CRC sequence (15 bits, 17 bits, 21 bits or 32 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 = ("F" if self.xl else "") + "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]]) if not self.is_valid_crc(crc_bits): self.putb([16, ['CRC is invalid']]) + elif self.xl and bitnum == self.crc_start + 32: + # Remember start of FCP sequence (see below). + self.ss_block = self.samplenum + + elif self.xl and bitnum == self.crc_start + 35: + x = self.crc_start + 32 + fcp = bitpack_msb(self.bits[x:x + self.crc_start + 36]) + + self.putb([30, ['Format check pattern: 0x%02X' % fcp, + 'FCP: 0x%02X' % fcp, 'FCP']]) + # CRC delimiter bit (recessive) - elif bitnum == (self.last_databit + self.crc_len + 1): - self.putx([12, ['CRC delimiter: %d' % can_rx, - 'CRC d: %d' % can_rx, 'CRC d']]) + elif not self.xl and bitnum == (self.crc_start + self.crc_len): + data = [12, ['CRC delimiter: %d' % can_rx, + 'CRC d: %d' % can_rx, 'CRC d']] + + if self.fd and self.brs: + from_bitrate = self.options['fd_bitrate'] + from_spp = self.options['fd_sample_point'] + to_bitrate = self.options['nominal_bitrate'] + to_spp = self.options['nominal_sample_point'] + + self.putx_brs(from_bitrate, from_spp, to_bitrate, to_spp, data) + else: + self.putx(data) + if can_rx != 1: self.putx([16, ['CRC delimiter must be a recessive bit']]) - if self.fd: - self.set_nominal_bitrate() + elif self.xl and bitnum == self.crc_start + 36: + self.putx([31, ['Data arbitration high: %d' % can_rx, + 'DAH: %d' % can_rx, 'DAH']]) + + elif self.xl and bitnum == self.crc_start + 37: + self.putx([32, ['Arbitration high 1: %d' % can_rx, + 'AH1: %d' % can_rx, 'AH1']]) + + elif self.xl and bitnum == self.crc_start + 38: + self.putx([33, ['Arbitration low 1: %d' % can_rx, + 'AL1: %d' % can_rx, 'AL1']]) + + elif self.xl and bitnum == self.crc_start + 39: + self.putx([34, ['Arbitration high 2: %d' % can_rx, + 'AH2: %d' % can_rx, 'AH2']]) # ACK slot bit (dominant: ACK, recessive: NACK) - elif bitnum == (self.last_databit + self.crc_len + 2): + elif self.xl and bitnum == self.crc_start + 40 or not self.xl and 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 self.xl and bitnum == self.crc_start + 41 or not self.xl and 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 self.xl and bitnum == self.crc_start + 42 or not self.xl and 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 self.xl and bitnum == self.crc_start - 1 + 42 + 7 or not self.xl and 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']]) @@ -281,37 +445,157 @@ def decode_standard_frame(self, can_rx, bitnum): self.dlc_start = 15 if bitnum == 15 and self.fd: - self.putx([7, ['Reserved: %d' % can_rx, 'R0: %d' % can_rx, 'R0']]) + self.xl = True if can_rx else False + + if self.xl: + self.putx([19, ['Extended data length format: %d' % can_rx, + 'XLF: %d' % can_rx, 'XLF']]) + self.dlc_start = 30 + self.dlc_field_len = 11 + else: + self.putx([7, ['Reserved: %d' % can_rx, 'R0: %d' % can_rx, 'R0']]) if bitnum == 16 and self.fd: - self.putx([7, ['Bit rate switch: %d' % can_rx, 'BRS: %d' % can_rx, 'BRS']]) + if self.xl: + self.putx([20, ['Reserved bit extended data field length format: %d' % can_rx, + 'resXL: %d' % can_rx, 'resXL']]) + else: + data = [7, ['Bit rate switch: %d' % can_rx, + 'BRS: %d' % can_rx, 'BRS']] + + if self.brs: + from_bitrate = self.options['nominal_bitrate'] + from_spp = self.options['nominal_sample_point'] + to_bitrate = self.options['fd_bitrate'] + to_spp = self.options['fd_sample_point'] + + self.putx_brs(from_bitrate, from_spp, to_bitrate, to_spp, data) + else: + self.putx(data) if bitnum == 17 and self.fd: - self.putx([7, ['Error state indicator: %d' % can_rx, 'ESI: %d' % can_rx, 'ESI']]) + if self.xl: + self.putx([21, ['Arbitration to data high: %d' % can_rx, 'ADH: %d' % can_rx, 'ADH']]) + else: + self.putx([7, ['Error state indicator: %d' % can_rx, 'ESI: %d' % can_rx, 'ESI']]) + + if self.xl: + if bitnum == 18: + self.putx([22, ['Data high bit 1: %d' % can_rx, 'DH1: %d' % can_rx, 'DH1']]) + + elif bitnum == 19: + self.putx([23, ['Data high bit 2: %d' % can_rx, 'DH2: %d' % can_rx, 'DH2']]) + + elif bitnum == 20: + self.putx([24, ['Data low bit 1: %d' % can_rx, 'DL1: %d' % can_rx, 'DL1']]) + + # Remember start of SDT (see below). + elif bitnum == 21: + self.ss_block = self.samplenum + + elif bitnum == 28: + sdt = bitpack_msb(self.bits[21:29]) + + self.putb([25, ['Service data unit type: %d' % sdt, + 'SDT: %d' % sdt, 'SDT']]) + + elif bitnum == 29: + self.putx([26, ['Simple/extended content: %d' % can_rx, + 'SEC: %d' % can_rx, 'SEC']]) # Remember start of DLC (see below). - elif bitnum == self.dlc_start: + if bitnum == self.dlc_start: self.ss_block = self.samplenum - # 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]) + # Data length code (DLC) + # - Classic CAN: number of bytes (0-8) + # - CAN-FD: 0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=12, 10=16, 11=20, 12=24, 13=32, 14=48, 15=64 + # - CAN-XL: Number of bytes minus one (0 - 2047) while 0 means 1 byte of data (0 bytes is not possible anymore) + + elif bitnum == self.dlc_start + self.dlc_field_len - 1: + self.set_dlc_and_crc_len(bitpack_msb(self.bits[self.dlc_start:self.dlc_start + self.dlc_field_len])) 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) + + if self.xl: + self.last_databit = 96 + (self.dlc + 1) * 8 + else: + self.last_databit = self.dlc_start + self.dlc_field_len - 1 + (dlc2len(self.dlc) * 8) + + self.crc_start = self.last_databit + 1 + + if self.fd and not self.xl: + 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']]) + if self.xl: + # Remember start of SBC (see below). + if bitnum == 41: + self.ss_block = self.samplenum + + # Stuff bit count (SBC) + elif bitnum == 43: + # SBC field + sbc_bits = self.bits[41:44] + p_gray_sbc = bitpack_msb(sbc_bits) + + parity = p_gray_sbc & 1 + gray_sbc = p_gray_sbc >> 1 + + # in contrast to FD CRC, odd parity scheme is used: + if not self.is_valid_odd_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 PCRC (see below). + elif bitnum == 44: + self.ss_block = self.samplenum + + # Preface CRC (PCRC) + elif bitnum == 56: + pcrc = bitpack_msb(self.bits[45:58]) + + self.putb([27, ['Preface CRC-13: 0x%04x' % pcrc, + 'PCRC-13: 0x%04x' % pcrc, 'PCRC-13']]) + + # Remember start of VCID (see below). + elif bitnum == 57: + self.ss_block = self.samplenum + + # Virtual CAN network channel identifier (VCID) + elif bitnum == 64: + vcid = bitpack_msb(self.bits[58:65]) + + self.putb([28, ['Virtual CAN network channel identifier: 0x%02X (%d)' % (vcid, vcid), + 'VCID: 0x%02X (%d)' % (vcid, vcid), 'VCID']]) + + # Remember start of AF (see below). + elif bitnum == 65: + self.ss_block = self.samplenum + + # Acceptance field (AF) + elif bitnum == 96: + af = bitpack_msb(self.bits[65:97]) + + self.putb([29, ['Acceptance field: 0x%02x (%d)' % (af, af), + 'AF: 0x%02x (%d)' % (af, af), 'AF']]) + # Remember all databyte bits, except the very last one. - elif bitnum in range(self.dlc_start + 4, self.last_databit): + if bitnum in range(97 if self.xl else self.dlc_start + 4, self.last_databit): self.ss_databytebits.append(self.samplenum) - # Bits 19-X: Data field (0-8 bytes, depending on DLC) + # Data field (0-2048 bytes, depending on DLC) # The bits within a data byte are transferred MSB-first. elif bitnum == self.last_databit: self.ss_databytebits.append(self.samplenum) # Last databyte bit. - for i in range(dlc2len(self.dlc)): - x = self.dlc_start + 4 + (8 * i) + for i in range(self.dlc + 1 if self.xl else dlc2len(self.dlc)): + x = (97 if self.xl else self.dlc_start + 4) + (8 * i) b = bitpack_msb(self.bits[x:x + 8]) self.frame_bytes.append(b) ss = self.ss_databytebits[i * 8] @@ -384,8 +668,18 @@ def decode_extended_frame(self, can_rx, bitnum): 'RB0: %d' % can_rx, 'RB0']]) elif bitnum == 35 and self.fd: - self.putx([7, ['Bit rate switch: %d' % can_rx, - 'BRS: %d' % can_rx, 'BRS']]) + data = [7, ['Bit rate switch: %d' % can_rx, + 'BRS: %d' % can_rx, 'BRS']] + + if self.brs: + from_bitrate = self.options['nominal_bitrate'] + from_spp = self.options['nominal_sample_point'] + to_bitrate = self.options['fd_bitrate'] + to_spp = self.options['fd_sample_point'] + + self.putx_brs(from_bitrate, from_spp, to_bitrate, to_spp, data) + else: + self.putx(data) elif bitnum == 36 and self.fd: self.putx([7, ['Error state indicator: %d' % can_rx, @@ -397,10 +691,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): @@ -425,26 +723,33 @@ def decode_extended_frame(self, can_rx, bitnum): return False - def handle_bit(self, can_rx): + def handle_bit(self, bitnum, can_rx): self.rawbits.append(can_rx) self.bits.append(can_rx) - # Get the index of the current CAN frame bit (without stuff bits). - bitnum = len(self.bits) - 1 - - if self.fd and can_rx: - if bitnum == 16 and self.frame_type == 'standard' \ - or bitnum == 35 and self.frame_type == 'extended': - self.dom_edge_seen(force=True) - self.set_fast_bitrate() - # If this is a stuff bit, remove it from self.bits and ignore it. if self.is_stuff_bit(): self.putx([15, [str(can_rx)]]) self.curbit += 1 # Increase self.curbit (bitnum is not affected). return else: - self.putx([17, [str(can_rx)]]) + if self.fd and can_rx and (bitnum == 16 and self.frame_type == 'standard' + or bitnum == 35 and self.frame_type == 'extended'): + from_bitrate = self.options['nominal_bitrate'] + from_spp = self.options['nominal_sample_point'] + to_bitrate = self.options['fd_bitrate'] + to_spp = self.options['fd_sample_point'] + + self.putx_brs(from_bitrate, from_spp, to_bitrate, to_spp, [17, [str(can_rx)]]) + elif self.fd and self.brs and bitnum == (self.crc_start + self.crc_len): + from_bitrate = self.options['fd_bitrate'] + from_spp = self.options['fd_sample_point'] + to_bitrate = self.options['nominal_bitrate'] + to_spp = self.options['nominal_sample_point'] + + self.putx_brs(from_bitrate, from_spp, to_bitrate, to_spp, [17, [str(can_rx)]]) + else: + self.putx([17, [str(can_rx)]]) # Bit 0: Start of frame (SOF) bit if bitnum == 0: @@ -510,10 +815,32 @@ def decode(self): self.dom_edge_seen(force = True) self.state = 'GET BITS' elif self.state == 'GET BITS': + bitnum = len(self.bits) # Get the index of the current CAN frame bit (without stuff bits). + # Wait until we're in the correct bit/sampling position. pos = self.get_sample_point(self.curbit) (can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}]) + if self.matched[1]: self.dom_edge_seen() if self.matched[0]: - self.handle_bit(can_rx) + if self.fd: + # FD-BRS bit: + if can_rx and (bitnum == 16 and self.frame_type == 'standard' + or bitnum == 35 and self.frame_type == 'extended'): + self.brs = True if can_rx else False + + if self.brs: + self.set_fd_bitrate() + + # FD-CRC delimiter + elif self.brs and bitnum == (self.crc_start + self.crc_len): + self.set_nominal_bitrate() + + self.handle_bit(bitnum, can_rx) + + if self.xl: + if bitnum == 17: # After ADH bit + self.set_xl_bitrate() + elif bitnum == self.crc_start + 35: # After last FCP bit + self.set_nominal_bitrate() \ No newline at end of file