ES87ME
ES87ME

Reputation: 57

CRC16 calculation in Tcl

Im trying to compute the CRC16 of a binary file.

I started by reading 2 Bytes from the Binary file and compute a CRC16 with a Polynome= 1021 and 0xFFFF Initial Value. I used a C code and tried to translate it to TCL. I couldnt use the bytes format because i get by the computation an error about using non numeric string. So i converted the bytes to strings.

proc main {}{
    # open binary file
    set file_read [open "$input_file" rb]
    while {1} {
        if {! [eof $fr]} {
          append binary_data [read $file_read 2]  
        }

         binary scan [string range $binary_data 0 1] H4 str_bin_data
         set CRC_data [CRC_calculation $str_bin_data]
         puts " CRC_data := $CRC_data"
    }   
}

proc CRC_calculation {str_bin_data} { 

    set Polynome 0x1021
    set Highbit   0x8000
    set CRC_data 0xFFFF
    set byte 0
    set bit  0
    set data_ln [string length $str_bin_data]
    # puts " data_ln := $data_ln"

    for {set byte 0} {$byte < $data_ln} {incr byte} {
        set CRC_data [ expr {$CRC_data ^ ([lindex $str_bin_data $byte] << 8)} ]                 
        for {set bit 8} {$bit > 0} {incr bit -1} {
            if {($CRC_data && $Highbit)} {
                set CRC_data [expr {($CRC_data << 1) ^ $Polynome}] 
            } else { 
                set CRC_data [expr {$CRC_data << 1}] 
            }           
        }   
        puts " byte_index := $byte"
        puts " CRC_data := $CRC_data"
    }

    return $CRC_data
} 

In C when i define a byte array example( first 8 Bytes in the binary file): unsigned char bytes[3]= {0x55,0x55,0x55,0x55}; then CRC = 0x82b8 In Tcl I dont get the correct value not even a 32 bit CRC value.

Here the C code that i m using:

#include<stdio.h>

#define Polynom 0x1021
#define Highbit 0x8000

unsigned short getCRC(const unsigned char data[])
{
    unsigned short rem = 0xFFFF;
    unsigned long  byte = 0;
    int bit = 0;
    for (byte = 0; byte < 3; ++byte) 
    {
        rem ^= (data[byte]<< 8); 
        for (bit = 8; bit > 0; --bit) 
        {
            if (rem & Highbit) 
                rem = (rem << 1) ^ Polynom;
            else
                rem = (rem << 1);
        }
    }
    return (rem);
}

int main() {
    int rem ;
    unsigned char data[]= {0x55,0x55,0x55,0x55};
    rem = getCRC (data);
    printf("%x", rem);

}

Upvotes: 1

Views: 442

Answers (1)

Donal Fellows
Donal Fellows

Reputation: 137567

There's a few problems. Firstly, and most importantly, the scanning of the binary data isn't right as we want to end up with unsigned bytes (for parallel operation with that C) and not hex characters. You'd be better with:

# I'm assuming you've got Tcl 8.6, this is how you read 2 bytes as unsigned chars
binary scan [read $file_read 2] "cu*" str_bin_data
# Process the list of parsed byte data here; I'm not sure if you want this in the loop or not

The other big problem is that your CRC calculation isn't correct.

proc CRC_calculation {str_bin_data} { 
    set Polynom 0x1021
    set Highbit 0x8000
    set MASK 0xFFFF;   # 16 bit mask; for clamping to C unsigned short range

    set rem 0xFFFF
    # Assume str_bin_data holds a list of unsigned char values
    foreach byte $str_bin_data {
        set rem [expr {$rem ^ ($byte << 8)}]
        foreach _ {7 6 5 4 3 2 1 0} {
            set rem [expr {
                (($rem << 1) ^ ($rem & $Highbit ? $Polynom : 0)) & $MASK
            }]
        }
    }
    return $rem
}

Key observation here? Tcl's numbers are arbitrary precision integers (and IEEE doubles, though not relevant here). This means that you need to clamp the range. Minimally, that would be an AND with 0xFFFF (16-bit mask) after any operation that can increase the number of bits in use, which is just << in this algorithm. That, plus the problems with converting the binary data in the first place, are why things weren't working for you. I've also switched to using foreach as that's fast and clearer for operations where “do every one of them” is the fundamental idea, and merged the inner bits into a single expr (yes, expr expressions can be multiline if you want).

The biggest single problem was that you were passing entirely the wrong thing to the CRC_calculation code. Changing the binary scan is vital.

Upvotes: 1

Related Questions