FC2ブログ
日々のサーバ管理、ソフトウェア開発技術力向上を目指すブログ

CRC-ITU-T(CRC-CCITT) 16bit計算アルゴリズム
最近、ひょんな所からCRC-16を計算する必要性が出てきたんで、
色々調べてたところ、複数サイトに計算方法があった。

でも、それ通りに計算してもうまく算出されない。
かなりデマな情報が掲載されており、まことに遺憾だなぁ、と。
※無駄な時間1日。

いちおう色々な情報を集約してうまくいったため、まとめ。

CRC16は用途にもよるが、CRC-ITU-T(別名CRC-16-CCITT)が
大体使用されている。

あと、CRCの計算条件として、
・計算開始する初期値
・計算方法(シフト方向)
・計算後出力演算
があるが、ここでは CRC 初期値が 0xFFFF、右シフト、出力反転なし、
の条件として計算を進める。

ものによっては初期値が0x0000だったり、途中のシフトが左だったり、
出力反転されたり、マチマチ。
※FF7のセーブデータチェックサムはCRC-16-CCITTの
※初期値0xFFFF、左シフト、出力反転、という仕様

計算方法提示前にまず前提。
 バッファにcrcReg(CRC算出用)、inData(入力値)、w(XOR結果用)の
  3つを用いる。
 crcRegは16ビット、inDataは8ビット、wは1ビットのバッファ。
 入力値は1バイトとする。

手順。
--------------------------------------------------------------
(1) 初期値として、crcRegに0xFFFFを入れる
(2) inDataに入力値(1バイト)を格納し、以下(3)~(6)を8回ループさせる
(3) crcRegとinDataの排他的論理和を取り、最下位ビットのみwに抽出。
  (w = crcReg ^ inData & 0x0001)
(4) crcRegを右に1ビットシフトしcrcRegに格納 (crcReg = crcReg >> 1)
(5) wが1であれば、crcRegと0x8408の排他的論理和を取った結果を
  crcRegに格納する。wが0なら何もしない。
(6) inDataを右に1ビットシフトする (inData = inData >> 1)
--------------------------------------------------------------
以上の処理によって残ったcrcRegの値が、求めるべきCRC値となる。
※0x8408は、CRCの多項式に対応した除数(反転)

【実際の計算例】
前提:0xFFFFを初期値、右シフト、出力反転なし、入力値を0xAA

(1) crcReg = 0xFFFF
(2) inData = 0xAA
(3-1) w = 0xFFFF ^ 0xAA & 0x0001 = 1
(4-1) crcReg = 0xFFFF >> 1 = 0x7FFF
(5-1) w=1なので、crcReg = 0x7FFF ^ 0x8408 = 0xFBF7
(6-1) inData = 0xAA >> 1 = 0x55

てことで、1回目ループではcrcReg=0xFBF7、inData=0x55
次、2回目ループ。
(3-2) w = 0xFBF7 ^ 0x55 & 0x0001 = 0
(4-2) crcReg = 0xFBF7 >> 1 = 0x7DFB
(5-2) w = 0 なので、変化なし(crcReg = 0x7DFBのまま)
(6-2) inData = 0x55 >> 1 = 0x2A

てことで、2回目ループではcrcReg=0x7DFB、inData=0x2A
あと同様に8回目ループまで実施すると、

3回目:crcReg=0xBAF5、inData=0x15
4回目:crcReg=0x5D7A、inData=0x0A
5回目:crcReg=0x2EBD、inData=0x05
6回目:crcReg=0x175E、inData=0x02
7回目:crcReg=0x0BAF、inData=0x01
8回目:crcReg=0x05D7、inData=0x00

てことで、CRC算出値は0x05D7になる。

追記:複数バイトの入力がある場合は、
 この算出値を次のcrcReg初期値として扱えばよい。

もし出力反転する前提なら、この値を反転すればよいので、
0x05D7 ^ 0xFFFF = 0xFA28 が算出値。

一方、左シフトの場合であるが、手順を以下のように変更すればよい。
(3) crcRegとinDataの排他的論理和を取り、最上位ビットのみwに抽出。
  w = (crcReg >> 15) ^ (inData >> 7) & 0x0001
  ※この時、inDataは8bit、crcRegは16bitであるから、
  ※inDataの上位ビットとcrcRegの上位ビットを合わせるために、
  ※それぞれ最上位ビットを最下位ビットまでシフトする
(4) crcRegを左に1ビットシフトしcrcRegに格納 (crcReg = crcReg << 1)
(5) wが1であれば、crcRegと0x1021の排他的論理和を取った結果をcrcRegに
  格納する。wが0なら何もしない。
(6) inDataを左に1ビットシフトする
※0x1021は、CRCの多項式に対応した除数(標準)

てことで、それぞれのパターンにおいて、入力値0xAAの算出結果一覧を掲載。
------------------------------------------
初期値 シフト方向 反転出力なし 反転出力あり
------------------------------------------
0x0000   右     0x0A50     0xF5AF     
0xFFFF   右     0x05D7     0xFA28
0x0000   左     0x14A0     0xEB5F
0xFFFF   左     0xF550     0x0AAF
------------------------------------------

Comment

 秘密にする

Track Back
TB*URL

Copyright © 趣味で始めるサーバ管理、ソフトウェア開発の成長ブログ. all rights reserved.