« Posts list

Computing the EtherCAT SubDevice SII/EEPROM checksum

This is an answer to this StackExchange question which I'm posting here instead as I refuse to add any more of my time or energy to that website.

I'm using Rust and the crc crate to calculate the checksum of the first 14 bytes of an EtherCAT SubDevice EEPROM. I first tried CRC-8/ROHC (crc::CRC_8_ROHC) as suggested by @craig-mcqueen but it gave me incorrect results when checking a few known-good EEPROM dump files I have.

I also iterated through every CRC_8_* constant in crc and none of them gave the correct result.

The SOEM EtherCAT MainDevice has its CRC implementation here which I used to cross check my Rust "implementation":

const ECAT_CRC: crc::Algorithm<u8> = crc::Algorithm {
    width: 8,
    poly: 0x07,
    init: 0xff,
    refin: false,
    refout: false,
    xorout: 0x00,
    check: 0x80,
    residue: 0x00,
};

const EEPROM_CRC: crc::Crc<u8> = crc::Crc::<u8>::new(&ECAT_CRC);

This is very close to CRC-8/ROHC but I had to change check from 0xd0 to 0x80. My checksums now line up with the presumably more exercised SOEM implementation and the actual value in the dumped EEPROM files so I believe it's correct, however I couldn't find a match in the catalogue which is weird.

I've also ported SOEM's implementation over to Rust if you don't want to use a whole crate:

fn calc_crc(crc: &mut u8, b: u8) {
    *crc ^= b;

    for _ in 0..8 {
        if (*crc & 0x80) > 0 {
            *crc = (*crc << 1) ^ 0x07;
        } else {
            *crc = *crc << 1;
        }
    }
}

fn sii_crc(buf: &[u8]) -> u16 {
    let mut crc = 0xffu8;

    for i in 0..14 {
        calc_crc(&mut crc, buf[i]);
    }

    u16::from(crc)
}

Hope this helps :)