SPI ハードウェア CRC 回路の MMC/SD カードへの応用 (4)
今回は、SPI のハードウェア CRC 計算回路に 8 次の生成多項式を設定した場合には避けられない、定義よりも 1 ビット分の「0」が多く入力される影響について考えます。
まず、定義通りに入力メッセージ 40 ビット + 追加の「0」7 ビット = 47 ビット分の入力を計算し終わった状態を考えます。
追加の「0」が入力されている状態では、原理的な回路でのシフトレジスタ最下位への外部入力がなく、LFSR 自体のフィードバックだけで閉じているのと同じになります。
したがって、下図のように、CRC 計算回路を単なる LFSR と考え、47 ビット目の計算終了時のシフトレジスタの内容を、ビット位置によって b7 から b0 で識別することにします。
この状態から、48 ビット目の追加の「0」を計算した後では、次の図のような状態になります。
47 ビット入力後の「b7」は、フィードバックによって戻されて、48 ビット入力後にはシフトレジスタの最下位ビットに入ることになります。
したがって、前回のように 47 ビット終了後にシフトレジスタの b7 が 1 か 0 かを検査することと、48 ビット入力後の状態でシフトレジスタの最下位ビットを検査することでは、両者に違いはなく、全く同じ意味を持つことが分かります。
47 ビット入力終了後の 8 次生成多項式の剰余を r8_47(x)、48 ビット入力後のそれを r8_48(x) とすると、両者の関係を式で表せば、
r8_48(x) = (x ・ r8_47(x)) mod ((x + 1) ・ g7(x))
となります。 ここで、g7(x) = x7 + x3 + 1 、つまり CRC-7 の生成多項式です。
47 ビット入力後の b7 に相当する、48 ビット入力後の b0 で場合分けをすれば、
- 48 ビット入力後の b0 = 0
r8_48(x) = x ・ r8_47(x)
- 48 ビット入力後の b0 = 1
r8_48(x) = x ・ r8_47(x) + ((x + 1) ・ g7(x))
= x ・ (r8_47(x) + g7(x)) + g7(x)
となります。
ここで、 48 ビット入力後の b0 = 0 の場合の x ・ r8_47(x) というのは、求めたい CRC-7 の結果の値 r8_47(x) を左に 1 ビットシフトしたものになっています。
これは、MMC/SD カードの規約の、求めた 7 ビットの CRC-7 の値を 6 バイト目の b7 から b1 に配置する条件に一致しています。
したがって、48 ビット入力後の b0 = 0 という条件では、CRC の値に修正は必要なく、さらにシフトの必要もなく、単に b0 に固定パターンの「1」を書き込むだけで、規定通りのフォーマットとなります。
それに対し、48 ビット入力後の b0 = 1 の場合には、CRC の値を修正する必要がありますが、上の式の
x ・ (r8_47(x) + g7(x))
は、47 ビット入力後の r8_47(x) に必要な修正パターン g7(x) を施したものに x を掛けて、1 ビット左シフトしたものになっています。 つまり、この項は、求める CRC-7 のパターンそのものになっています。
しかし、そのあとに g7(x) という余計な項が付いていますから、この項の寄与を差し引く必要があります。 具体的には CRC-7 の生成多項式のビットパターンとエクスクルーシブ OR を取ります。
結局、これらを総合して、
// CRC-7: g(x) = x**7 + x**3 + 1 // = {7, 3, 0} // = 0x89 // = 0b10001001 #define GX_CRC7 (0x89) // get CRC value from Tx CRC register crc_value = SPI_GetCRC(SPI1, SPI_CRC_Tx); if (0x01 & crc_value) { // adjustment required ? crc_value ^= GX_CRC7; // adjust CRC } // if (0x01 & .. . crc_value |= 0x01; // force the last bit to '1' SPI_I2S_SendData(SPI1, crc_value); // send CRC byte
のようなプログラムで、CRC の補正と送信ができます。
次回は、「ぷち FatFs」に組み込んだ実際のプログラムの例を示します。