CRC-Prüfung (2): Detaillierte Erläuterung der CRC32-Tabellensuchmethode, Code-Implementierung und CRC-Umkehr

Bei aktuellen CPUs sind CRC-Prüfungen grundsätzlich auf der Hardware implementiert. Wir möchten den CRC-Code jedoch weiterhin mithilfe von Software implementieren, damit wir die darin enthaltenen Prinzipien besser verstehen können. In diesem Abschnitt wird daher ausführlich erläutert, wie die Nachschlagetabellenmethode verwendet wird, um die CRC-32-Verifizierung über die Software zu realisieren. Darüber hinaus gibt es bei CRC auch eine Umkehrsituation. Tatsächlich gibt es keinen großen Unterschied zwischen Umkehrung und Nichtumkehrung, hauptsächlich aufgrund der unterschiedlichen Anforderungen und Standards.

1 Finde das Muster

Schauen wir uns noch einmal das Beispiel im vorherigen Abschnitt an, der Berechnungsprozess der Dividende ist 100100und der Divisor ist 1101:
Fügen Sie hier eine Bildbeschreibung ein
Lassen Sie uns das Gesetz finden und darüber nachdenken, wie es im Computer implementiert werden soll. Es ist nicht schwer, die folgenden Regeln zu finden:

  • Das höchste Bit des Divisors muss 1 sein, da wir die höchste Ordnungszahl des Polynoms verwenden, das es darstellt
  • Wenn für den Dividenden das höchste Bit nicht 1 ist, wird er „dividieren“, nichts unternehmen, ein Bit nach links verschieben und weiter urteilen.
  • Da die XOR-Operation durchgeführt wird, muss das aktuelle Ergebnis nicht unbedingt den Dividenden „subtrahieren“. Es muss nur erfüllt werden, dass das höchste Bit des Divisors 1 ist, dann eine XOR-Operation mit dem Dividenden durchgeführt und dann ein Bit verschoben werden die linke

2 Prinzip der Nachschlagetabellenmethode

Wenn wir CRC in Software implementieren möchten, ist es sehr ineffizient, diese Berechnungen einzeln durchzuführen. Daher fragen wir uns, ob wir alle 8 Bits berechnen können, da jede spezifische Zahl und der Dividend 8 Mal durchlaufen werden. Die durch die Verschiebung erhaltenen XOR-Ergebnisse Bedienung sind alle gleich.
(1) Warum nicht alle 16 Bits zählen?
Dies spart 2 16 2^{16}216 Daten vom Typ uint16_t, eine Tabelle hat 128 KB, was größer ist als der Flash vieler Single-Chip-Mikrocomputer
(2) Was ist die theoretische Grundlage für die Berechnung alle 8 Bits?
Wir wissen, dassCRC (0) = 0 CRC(0 )=0CRC ( 0 )=0 , und dann können wir diese Formel aus den oben analysierten Merkmalen ableiten:CRC ( A + B ) = CRC ( A ) + CRC ( B ) CRC(A+B) = CRC(A) + CRC(B)CRC ( A+B )=CRC ( A )+CRC ( B )

Wenn wir nun zum Beispiel CRC ( 0 × abcdef 12 ) CRC(0×abcdef12) finden möchtenCRC ( 0×ab c d e f 12 ) , er kann äquivalent sein zu:
CRC ( 0 × abcdef 12 ) = CRC ( 0 × ab 000000 ) + CRC ( 0 × 00 cdef 12 ) = CRC ( 0 × ab ) + CRC ( 0 × cd ) + CRC ( 0 × ef ) + CRC ( 0 × 12 ) CRC(0×abcdef12) = CRC(0×ab000000) + CRC(0×00cdef12) = CRC(0×ab) + CRC(0×cd ) + CRC(0×ef) + CRC(0×12)CRC ( 0×ab c d e f 12 )=CRC ( 0×ab 000000 )+CRC ( 0×00 c d e f 12 )=CRC ( 0×ab )+CRC ( 0×c d )+CRC ( 0×e f )+CRC ( 0×12 )

Wenn wir eine 8-Bit-Nachschlagetabelle definieren, können wir die ursprünglichen 32 Schiebe- und XOR-Operationen auf das Vierfache verkürzen.

3 CRC32-Code C-Sprachimplementierung

3.1 CRC32-Tabellengenerierung

Nach der vorherigen Analyse wissen wir, dass wir lediglich eine Modulo-Zwei-Division für jede Zahl im Bereich von uint8_t (0~255) durchführen und das Ergebnis dann in einer Tabelle vom Typ uint8_t und der Größe 256 speichern müssen. Wir müssen nur beurteilen, ob das höchste Bit des CRC 1 ist. Wenn es 0 ist, verschieben Sie es um ein Bit nach links. Wenn es 1 ist, verschieben Sie zuerst das CRC um 1 Bit nach links und führen Sie dann eine XOR-Operation durch mit dem Polynom. Der CRC32-Tabellengenerierungscode lautet wie folgt:

void GenerateTable(uint32_t polynomial)
{
	for (int byte = 0; byte < 256; ++byte)
	 {
		uint32_t crc = byte;
		
		for (int bit = 32; bit > 0; --bit)
		{
			if (crc & 0x80000000)
			{
				  crc = (crc << 1) ^ polynomial;
			}
			else
			{
				  crc <<= 1;
			}
		}
		crcTable[byte] = crc;
	 }
}

Wenn Sie den obigen Code mit dem Bild im vorherigen Beispiel vergleichen, werden sich wahrscheinlich viele Leute fragen, warum er nicht ausgeführt wird, wenn das höchste Bit des CRC 1 ist crc = (crc ^ polynomial) << 1?

Vergessen Sie nicht, dass für CRC32 die Binärdatei, die dem Polynom entspricht, 33 Bits hat. Tatsächlich können wir nicht eine einzelne uint32_tVariable verwenden, um dieses Polynom zu speichern. Wir wissen jedoch, dass das höchste Bit des CRC32-Polynoms 1 sein muss, daher speichern wir hier die unteren 32 Bits als polynomial. Wenn das höchste Bit im Code CRC1 ist, muss das Ergebnis seiner XOR-Verknüpfung mit dem höchsten Bit von CRC32 ebenfalls 0 sein. Hier also zuerst CRCum ein Bit nach rechts verschieben, dann mit dem Polynom XOR-verknüpfen und auf diese Weise 32 Mal durchlaufen, um das entsprechende polynomialunter dem Polynom zu erhalten und es in zu speichern.byteCRCcrcTable

3.2 Implementierung des CRC-Tabellen-Suchcodes

Mit der vorherigen Tabelle können wir die folgende Funktion verwenden, um den CRC32 einer Zeichenfolge mit einer Länge von zu berechnen len.msg

unsigned int calcMsgCRC(char *msg, unsigned int len)
{
    unsigned long crc = 0;
    for (int n = 0; n < len; n++)
    {
    	uint8_t c = msg[n] & 0xff;
        crc = crcTable[(crc >> 24) ^ c] ^ (crc << 8);
    }
    return crc;
}
  • Im CRC-Algorithmus einiger Protokolle hat der CRC einen festen Anfangswert, und nachdem das CRC-Ergebnis erhalten wurde, muss auch eine feste Zahl XOR-verknüpft werden.

Darüber hinaus wird in einigen Fällen, beispielsweise beim Abrufen der neuesten Firmware über die serielle Schnittstelle zum Aktualisieren des Systems, die Integrität der Firmware auch durch CRC überprüft. Bei einigen Geräten ist nicht so viel Speicher vorhanden, um den gesamten Puffer zu speichern und dann die CRC-Prüfung des gesamten Puffers zu berechnen. In diesem Fall können wir den CRC Segment für Segment berechnen und dann das Ergebnis des vorherigen CRC dem unsigned long crcAnfangswert in der Funktion zuweisen. Das heißt, calcMsgCRCder Funktion wird ein weiterer Parameter hinzugefügt lastCRC.

4 CRC-Inversion

Manchmal stellen Sie möglicherweise fest, dass sich die CRC-Tabelle einiger Standardprotokolle von der von Ihnen generierten unterscheidet. Dies liegt wahrscheinlich daran, dass CRC zwei Implementierungen hat:

  • Nicht invertierter CRC: Jedes Datenbit wird der Reihe nach vom höchsten bis zum niedrigsten Bit verarbeitet
  • CRC umkehren: Die Verarbeitungsreihenfolge jedes Datenbytes oder -bits kann nach Bedarf umgekehrt werden.

4.1 Standard-Inversionsfunktion von CRC32

Ich habe die Definition dieser Inversion nicht gefunden, aber ich habe den Code gefunden. Lassen Sie uns also anhand des Codes verstehen, wie die sogenannte Inversionsfunktion invertiert wird.

  • Natürlich können diese Inversionen selbst definiert werden. Wenn Sie beispielsweise den Little-Endian-CRC in Big-Endian konvertieren, wird jedes Bit invertiert usw. Die hier vorgestellte Inversionsmethode wird im CRC-Algorithmus verwendet, der in einigen bekannten Protokollen verwendet wird.
uint32_t Reverse(uint32_t value)
{
  value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1);
  value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2);
  value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4);
  value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
  value = (value >> 16) | (value << 16);
  return value;
}

(1) Nehmen Sie die ungeraden und geraden Bits des Werts heraus, verschieben Sie das ungerade Bit um 1 Bit nach rechts und das gerade Bit um 1 Bit nach links und führen Sie dann eine bitweise ODER-Operation an ihnen durch. Dadurch werden die Werte der ungeraden und geraden Bits vertauscht.
(2) Gruppieren Sie alle 2 Bits des Werts und kehren Sie die Bitreihenfolge der hohen und niedrigen Bits jeder Gruppe um. Verwenden Sie in ähnlicher Weise bitweise UND- und bitweise ODER-Operationen, um High- und Low-Bits auszutauschen.
(3) Gruppieren Sie alle 4 Bits des Werts und kehren Sie die Bitreihenfolge der hohen und niedrigen Bits jeder Gruppe um.
(4) Gruppieren Sie alle 8 Bits des Werts und kehren Sie die Bitreihenfolge der hohen und niedrigen Bits jeder Gruppe um.
(5) Kehren Sie die Bitreihenfolge der oberen 16 Bits und der unteren 16 Bits des Werts um

4.2 CRC-Eingangs-/Ausgangsinvertierung

Darüber hinaus können wir den CRC bei der Eingabe umkehren oder den CRC bei der Ausgabe umkehren. Natürlich können wir auch beide umkehren. Daher muss die umgekehrte CRC32-Tabellengenerierungsfunktion zu diesem Zeitpunkt leicht geändert werden:

void GenerateTable(uint32_t polynomial, bool reflectIn, bool reflectOut)
{
    for (int byte = 0; byte < 256; ++byte)
    {
        uint32_t crc = (reflectIn ? (Reverse(uint32_t(byte)) >> 24) : byte);

        for (int bit = 32; bit > 0; --bit)
        {
            if (crc & 0x80000000)
            {
                crc = (crc << 1) ^ polynomial;
            }
            else
            {
                crc <<= 1;
            }
        }
        crcTable[byte] = (reflectOut ? Reverse(crc) : crc);
    }
}

Für die Berechnungsfunktion zum Umkehren des CRC (vorausgesetzt, dass sowohl Eingabe als auch Ausgabe umgekehrt sind) sollte sie wie folgt geändert werden:

unsigned int calcMsgCRCReverse(char *msg, unsigned int len)
{
    unsigned long crc = 0;
    for (int n = 0; n < len; n++)
    {
        uint8_t c = msg[n] & 0xff;
        c = Reverse(c);
        crc = crcTable[(crc ^ c) & 0xFF] ^ (crc >> 8);
    }
    return Reverse(crc);
}

Bei Verwendung von invertiertem CRC oder nicht invertiertem CRC unterscheidet sich auch die Methode zum Generieren und Nachschlagen der CRC-Tabelle. In der Praxis gibt es zwischen diesen beiden Methoden keinen offensichtlichen Unterschied in der Leistung oder Genauigkeit, und die Wahl der Methode hängt hauptsächlich von den Anforderungen und Standards der Anwendung ab.

Supongo que te gusta

Origin blog.csdn.net/tilblackout/article/details/131198350
Recomendado
Clasificación