rlw has asked for the wisdom of the Perl Monks concerning the following question:

I've written a CRC calculator to match one implemented in an electronic control module. The ultimate purpose is for downloading large amounts of data into the module.

First draft: CRC the whole file and download the whole file to the module. This works just fine, CRC-wise. However, the data is sparse, so takes much longer than truely needed to download.

Second draft: Partion file into 32 byte blocks; CRC and download only the blocks with actual data. The data downloads fine, but the CRC check is "off by one".

That is to say that when the CRC checker in the module performs the check on the data blocks received, the final "total" CRC value is 0xFFFF. It should be 0x0000.

Note that this error only occures when my CRC generator is run on a block-by-block basis, rather than the whole file all at once:

@data = read_data($infile); # @data is global $data[0x6FFE] = 0; # zero CRC $data[0x6FFF = 0; # locations $crc = crc16(0x0000, 0x6FFF, 0); # works $crc = 0; for ($a = 0x0000; $a < 0x6fff; $a += 32) { # final value next if (unusedBlock($a, 32); # off by one $crc = crc16($a, $a + 32, $crc); } $data[0x6FFE] = $crc >> 8; # save $data[0x6FFF] = $crc & 0xFF; # CRC write_data($outfile); # or write_data_blocks($outfile);

Obviously, the block-by-block CRC is based on fewer bytes than the full CRC, so is, as expected, different.

The module software is designed handle the data download in blocks, and, therefore, runs it CRC calculation block-by-block. In fact, even when the whole file is downloaded, it is still blocked. The difference is that all blocks are sent for the whole file, while only a subset for block-by-block.

Below are my Perl CRC routine (called by the above code) and the C source of the module's CRC routine.

sub crc16 ($$$) { my ($startadr, $endadr, $crc) = @_; my ($cadr, $adr, $i, $u, $d); for ($adr = $startadr; $adr <= $endadr; $adr++) { if (defined($data[$adr])) { $d = $data[$adr] & 0xFF; } else { $d = 0xFF; } $i = $crc >> 8; $crc <<= 8; $crc &= 0xFFFF; $crc |= $d; $crc ^= $crctable[$i]; } return $crc; }
void VerifyFlashBlock(UINT8 len, UINT16 address) { UINT8 byte_value; UINT8 crc_table_index; for (;len > 0; len--) { byte_value = _READ_BYTE_FROM_FLASH(address); crc_table_index = crc >> 8; crc = (crc << 8) | byte_value; crc = crc ^ _Crc_table[crc_table_index]; address++; buffer++; } }

Replies are listed 'Best First'.
Re: CRC off-by-one
by Anonymous Monk on Mar 09, 2005 at 21:10 UTC
    $crc = crc16($a, $a + 32, $crc); ... for ($adr = $startadr; $adr <= $endadr; $adr++) {
    Fencepost error. Should be $a+31, or $addr < $endaddr. Whether this fixes your problem, I don't know.
Re: CRC off-by-one
by Popcorn Dave (Abbot) on Mar 09, 2005 at 20:37 UTC
    This is only a stab, but shouldn't you be adding 0x0020 rather than 32 decimal? Could your loop be incrementing by 32 hex rather than decimal? Is it possible that you're reading too much?

    Useless trivia: In the 2004 Las Vegas phone book there are approximately 28 pages of ads for massage, but almost 200 for lawyers.