Das FSoE-Protokoll (Functional Safety over EtherCAT) verwendet einen 16-Bit-CRC zum Schutz seiner Sicherheitsmeldungen. Implementierungen greifen typischerweise auf zwei vorberechnete 256-Einträge-Lookup-Tabellen zurück, konventionell CRC16_TABLE und CRC16_TABLE2 genannt. Dieser Beitrag erklärt genau, wie diese Tabellen aus dem CRC-Polynomial abgeleitet werden – keine per Hand eingetippten magischen Konstanten, nur das Polynomial und der Algorithmus.
Das Polynomial
Der FSoE-CRC basiert auf dem 17-Bit-Polynomial
$$ P(x) = x^{16} + x^{13} + x^{12} + x^{11} + x^{8} + x^{7} + x^{5} + x^{4} + x^{2} + x + 1 $$das, gepackt als hexadezimaler Wert mit dem impliziten $x^{16}$-Term, 0x139B7 ergibt. Die im Shift-Register verwendete 16-Bit-Darstellung (das Polynomial ohne den $x^{16}$-Term) ist 0x39B7.
Der CRC wird MSB-first verarbeitet (die „normale" bzw. nicht-reflektierte Richtung): jedes Nachrichten-Bit wird am höchstwertigen Ende des Arbeitsregisters entnommen, und das Polynomial wird immer dann hinein-XORiert, wenn das oben herausgeschobene Bit 1 ist.
Was ein CRC-Lookup-Tabellen-Eintrag ist
Eine byte-weise CRC-Lookup-Tabelle erlaubt es, ein ganzes Byte (8 Bits) mit einem einzigen Tabellen-Lookup zu verarbeiten, statt 8 einzelne Bit-Shifts durchzuführen. Für einen MSB-first-CRC der Breite $W = 16$ ist der Eintrag für den Bytewert $i$ der Rest der Polynomialdivision
$$ \text{table}[i] = \left( i \cdot x^{W + 8k} \right) \bmod P(x) $$wobei $k$ auswählt, für welche Byte-Position innerhalb einer Multi-Byte-Gruppe die Tabelle gedacht ist:
- Der Faktor $x^{W} = x^{16}$ trägt der 16-Bit-Breite des CRC-Registers Rechnung (der Rest ist 16 Bits breit).
- Der Faktor $x^{8k}$ trägt dem Umstand Rechnung, dass das Byte $k$ Byte-Slots weiter rechts (niedrigwertiger) innerhalb der Gruppe liegt, sodass $8k$ zusätzliche Bit-Positionen an Reduktion nötig sind.
Rechnerisch wird dies umgesetzt, indem $i$ in das obere Byte eines 16-Bit-Registers geladen wird ($\text{crc} = i \ll 8$) und dann $8 + 8k$ Left-Shifts durchgeführt werden, wobei jedes Mal das Polynomial in das Register hinein-XORiert wird, sobald Bit 15 (das Bit, das oben das Register verlässt) gesetzt ist:
$$ \text{crc} \gets \begin{cases} (\text{crc} \ll 1) \oplus \texttt{0x39B7} & \text{if } \text{crc} \mathbin{\&} \texttt{0x8000} \\ \text{crc} \ll 1 & \text{otherwise} \end{cases} $$Das ist exakt die schriftliche Division von $i \cdot x^{8+8k}$ durch $P(x)$, wobei nur der 16-Bit-Rest behalten wird.
Die beiden FSoE-Tabellen
Die FSoE-Implementierung verwendet zwei Tabellen, entsprechend zwei verschiedenen Werten von $k$:
CRC16_TABLE — die gewöhnliche byte-weise Tabelle ($k = 0$)
$$
\text{CRC16\_TABLE}[i] = \left( i \cdot x^{16} \right) \bmod P(x)
$$Diese wird mit 8 Left-Shifts von $i \ll 8$ berechnet. Sie ist die Standard-Lookup-Tabelle, mit der der CRC ein Byte nach dem anderen aktualisiert wird:
$$ \text{crc} \gets (\text{crc} \ll 8) \oplus \text{CRC16\_TABLE}[\text{byte}] $$CRC16_TABLE2 — die Slice-by-4-Tabelle ($k = 3$)
$$
\text{CRC16\_TABLE2}[i] = \left( i \cdot x^{40} \right) \bmod P(x)
$$Diese wird mit 32 Left-Shifts von $i \ll 8$ berechnet (da $8 + 8 \cdot 3 = 32$). Sie ist die Slice-by-4-Tabelle für das niederwertigste Byte einer 4-Byte-Gruppe. Äquivalent dazu ist sie das Ergebnis der vierfachen Anwendung des gewöhnlichen CRC16_TABLE-Lookups, beginnend bei Byte $i$ – das heißt, Verarbeitung von Byte $i$ gefolgt von drei Null-Bytes:
In einem Slice-by-4-Aktualisierungsschritt, der vier Bytes auf einmal verarbeitet, wird der CRC aktualisiert als:
$$ \text{crc} \gets \text{CRC16\_TABLE2}[\text{crc}_{\text{lo}} \oplus b_0] \oplus \text{CRC16\_TABLE}[\text{crc}_{\text{hi}} \oplus b_1] \oplus \ldots $$wobei $b_0$ das niederwertigste Eingabe-Byte ist (dasjenige, das die meisten zusätzlichen Reduktionen benötigt, daher $k=3$).
Generator-Programm
Das folgende C-Programm erzeugt beide Tabellen aus dem Polynomial 0x39B7 und gibt sie als C-Quelltext aus. Der Kern ist eine einzige Funktion, table_entry(i, shifts), welche die oben beschriebene MSB-first-Polynomial-Langdivision durchführt. CRC16_TABLE wird mit shifts = 8 ($k=0$) und CRC16_TABLE2 mit shifts = 32 ($k=3$) aufgebaut.
/*
* GenTables.c
* ============
*
* Generator-Programm für die FSoE- (Functional Safety over EtherCAT)
* CRC-16-Lookup-Tabellen.
*
* Dieses Programm gibt, kompiliert und ausgeführt, C-Quellcode für eine
* zweite Quelldatei (Tables.c) aus, die zwei 256-Einträge-, 16-Bit-
* Lookup-Tabellen enthält. Die Tabellen werden ausschließlich aus dem
* CRC-Polynomial abgeleitet; im erzeugten Output erscheinen keine per
* Hand eingetippten Konstanten.
*
*
* Der CRC
* --------
*
* Der FSoE-CRC ist ein 16-Bit-CRC, der das 17-Bit-Polynomial
*
* P(x) = x^16 + x^13 + x^12 + x^11 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
*
* verwendet, das in gepackter hexadezimaler Form (mit dem impliziten
* x^16-Term entfernt) gleich
*
* 0x39B7 (16 Bits)
*
* ist, oder, inklusive des x^16-Terms,
*
* 0x139B7 (17 Bits).
*
* Der CRC wird MSB-first verarbeitet (gelegentlich als „normale" bzw.
* „nicht-reflektierte" Richtung bezeichnet): jedes Nachrichten-Bit wird
* am höchstwertigen Ende des Arbeitsregisters entnommen, und das
* Polynomial wird hinein-XORiert, sobald das herausgeschobene Bit 1 ist.
*
*
* Wie die Tabellen erzeugt werden
* -------------------------------
*
* Eine CRC-Lookup-Tabelle erlaubt es, ein ganzes Byte (8 Bits) mit einem
* einzigen Tabellen-Lookup zu verarbeiten, statt 8 einzelne Bit-Shifts
* durchzuführen. Für einen MSB-first-CRC ist der Eintrag für den Bytewert
* `i` der Rest der Polynomialdivision
*
* table[i] = ( i * x^(16 + 8*k) ) mod P(x)
*
* wobei `k` auswählt, für welche Byte-Position innerhalb einer Multi-Byte-
* Gruppe die Tabelle gedacht ist. Der Faktor x^16 trägt der 16-Bit-Breite
* des CRC-Registers Rechnung (der Rest ist 16 Bits breit), und der Faktor
* x^(8*k) trägt dem Umstand Rechnung, dass das Byte `k` Byte-Slots weiter
* rechts (niedrigwertiger) innerhalb der Gruppe liegt, sodass `8*k`
* zusätzliche Bit-Positionen an Reduktion nötig sind.
*
* Rechnerisch wird dies umgesetzt, indem `i` in das obere Byte eines
* 16-Bit-Registers geladen wird (crc = i << 8) und dann `8 + 8*k`
* Left-Shifts durchgeführt werden, wobei jedes Mal das Polynomial in das
* Register hinein-XORiert wird, sobald das Bit, das oben das Register
* verlässt (Bit 15), gesetzt ist:
*
* for (n = 0; n < 8 + 8*k; n++)
* crc = (crc & 0x8000) ? ((crc << 1) ^ POLY) : (crc << 1);
*
* Die beiden hier erzeugten Tabellen sind:
*
* CRC16_TABLE (k = 0): table[i] = (i * x^16) mod P -- 8 Shifts
* Dies ist die gewöhnliche byte-weise Lookup-Tabelle. Sie dient
* dazu, den CRC ein Byte nach dem anderen zu aktualisieren:
* crc = (crc << 8) ^ CRC16_TABLE[byte];
*
* CRC16_TABLE2 (k = 3): table[i] = (i * x^40) mod P -- 32 Shifts
* Dies ist die Slice-by-4-Tabelle für das niederwertigste Byte
* einer 4-Byte-Gruppe. Äquivalent dazu ist sie das Ergebnis der
* vierfachen Anwendung des gewöhnlichen Tabellen-Lookups, beginnend
* bei Byte `i` (d. h. Verarbeitung von Byte `i` gefolgt von drei
* Null-Bytes). Sie dient im Slice-by-4-Aktualisierungsschritt, der
* vier Bytes auf einmal verarbeitet, folgendem Zweck:
* crc ^= (input32 & 0xFF); // niederwertigstes
* crc = CRC16_TABLE2[crc & 0xFF] // Byte einfügen,
* ^ CRC16_TABLE [(input32 >> 8) & 0xFF] // ...dann die
* ^ <slice k=1 table>[(input32 >> 16) & 0xFF] // restlichen
* ^ <slice k=2 table>[(input32 >> 24) & 0xFF] // Bytes.
* (Für die FSoE-Implementierung werden nur CRC16_TABLE und
* CRC16_TABLE2 benötigt; die Slice-Tabellen k=1 und k=2 werden hier
* nicht erzeugt.)
*
* Bauen und ausführen:
* cc -std=c99 -O2 -o GenTables GenTables.c
* ./GenTables > Tables.c
*/
#include <stdint.h>
#include <stdio.h>
/* Untere 16 Bits des Polynomials 0x139B7 (der x^16-Term ist implizit). */
#define POLY 0x39B7U
/*
* Berechnet einen MSB-first-CRC-16-Tabellen-Eintrag.
*
* i : der Bytewert (0..255), der als Tabellenindex dient.
* shifts : die Anzahl der durchzuführenden Left-Shifts = 8 + 8*k, wobei k
* die Byte-Position innerhalb eines 4-Byte-Slices ist (k=0 -> 8
* Shifts, k=3 -> 32 Shifts).
*
* Das Byte wird in das obere Oktett eines 16-Bit-Registers platziert und
* `shifts`-mal nach links geschoben. Sobald vor einem Shift das oberste
* Bit (Bit 15) gesetzt ist, wird danach das Polynomial hinein-XORiert,
* was exakt der schriftlichen Division von (i * x^(8+shifts)) durch P(x)
* entspricht, wobei nur der 16-Bit-Rest behalten wird.
*/
static uint16_t table_entry(uint8_t i, int shifts)
{
uint16_t crc = (uint16_t)i << 8;
int n;
for (n = 0; n < shifts; ++n) {
if (crc & 0x8000U)
crc = (uint16_t)((crc << 1) ^ POLY);
else
crc = (uint16_t)(crc << 1);
}
return crc;
}
/*
* Füllt eine 256-Einträge-Tabelle, indem jeder Eintrag unabhängig berechnet
* wird. `shifts` wählt aus, welche Slice-Tabelle aufgebaut wird (8 für k=0,
* 32 für k=3).
*/
static void build_table(uint16_t table[256], int shifts)
{
int i;
for (i = 0; i < 256; ++i)
table[i] = table_entry((uint8_t)i, shifts);
}
/* Gibt eine 256-Einträge-uint16_t-Tabelle als C-Quelltext aus, 8 Werte pro Zeile. */
static void print_table(const char *name, const uint16_t table[256])
{
int i;
printf("const uint16_t %s[256] = {\n", name);
for (i = 0; i < 256; i += 8) {
printf(" 0x%04X, 0x%04X, 0x%04X, 0x%04X, "
"0x%04X, 0x%04X, 0x%04X, 0x%04X,\n",
table[i], table[i+1], table[i+2], table[i+3],
table[i+4], table[i+5], table[i+6], table[i+7]);
}
printf("};\n");
}
int main(void)
{
uint16_t table1[256]; /* k = 0: (i * x^16) mod P */
uint16_t table2[256]; /* k = 3: (i * x^40) mod P */
build_table(table1, 8); /* gewöhnliche byte-weise Tabelle */
build_table(table2, 32); /* Slice-by-4, niederwertigstes Byte */
/*
* Die zweite Quelldatei (Tables.c) ausgeben. Der hier geschriebene
* Header-Kommentar dokumentiert genau, was die Tabellen sind und wie
* sie mit dem Polynomial zusammenhängen, sodass Tables.c in sich
* geschlossen ist.
*/
printf("/*\n");
printf(" * Tables.c (auto-generated by GenTables.c -- do not edit)\n");
printf(" * =======================================================\n");
printf(" *\n");
printf(" * Two 256-entry, 16-bit CRC lookup tables for the FSoE\n");
printf(" * (Functional Safety over EtherCAT) CRC-16.\n");
printf(" *\n");
printf(" * Polynomial\n");
printf(" * -----------\n");
printf(" * P(x) = x^16 + x^13 + x^12 + x^11 + x^8 + x^7 + x^5\n");
printf(" * + x^4 + x^2 + x + 1\n");
printf(" * packed (without the x^16 term): 0x39B7\n");
printf(" * packed (with the x^16 term): 0x139B7\n");
printf(" *\n");
printf(" * Direction: MSB-first (\"normal\", non-reflected).\n");
printf(" *\n");
printf(" * Table construction\n");
printf(" * ------------------\n");
printf(" * Each entry is the remainder of a polynomial division:\n");
printf(" *\n");
printf(" * table[i] = ( i * x^(16 + 8*k) ) mod P(x)\n");
printf(" *\n");
printf(" * computed by loading i into the high byte of a 16-bit\n");
printf(" * register (crc = i << 8) and performing 8 + 8*k left\n");
printf(" * shifts, XORing the polynomial in whenever bit 15 is set\n");
printf(" * before a shift:\n");
printf(" *\n");
printf(" * for (n = 0; n < 8 + 8*k; n++)\n");
printf(" * crc = (crc & 0x8000) ? ((crc << 1) ^ 0x39B7)\n");
printf(" * : (crc << 1);\n");
printf(" *\n");
printf(" * Tables in this file\n");
printf(" * -------------------\n");
printf(" * CRC16_TABLE (k = 0, 8 shifts):\n");
printf(" * table[i] = (i * x^16) mod P.\n");
printf(" * Ordinary byte-wise lookup table. Used to update the\n");
printf(" * CRC one byte at a time:\n");
printf(" * crc = (crc << 8) ^ CRC16_TABLE[byte];\n");
printf(" *\n");
printf(" * CRC16_TABLE2 (k = 3, 32 shifts):\n");
printf(" * table[i] = (i * x^40) mod P.\n");
printf(" * Slice-by-4 table for the least-significant byte of a\n");
printf(" * 4-byte group; equivalently the result of applying the\n");
printf(" * ordinary table lookup four times starting from byte i\n");
printf(" * (byte i followed by three zero bytes).\n");
printf(" */\n");
printf("\n");
printf("#include <stdint.h>\n");
printf("\n");
print_table("CRC16_TABLE", table1);
printf("\n");
print_table("CRC16_TABLE2", table2);
return 0;
}Bauen und ausführen:
cc -std=c99 -O2 -o GenTables GenTables.c
./GenTables > Tables.cErzeugte Tabellen
Der Output des Generators ist die folgende in sich geschlossene C-Quelldatei. Beide Tabellen stimmen exakt mit den FSoE-Referenztabellen überein – jeder der 256 Einträge pro Tabelle.
/*
* Tables.c (automatisch erzeugt von GenTables.c -- nicht bearbeiten)
* ==================================================================
*
* Zwei 256-Einträge-, 16-Bit-CRC-Lookup-Tabellen für den
* FSoE- (Functional Safety over EtherCAT) CRC-16.
*
* Polynomial
* ----------
* P(x) = x^16 + x^13 + x^12 + x^11 + x^8 + x^7 + x^5
* + x^4 + x^2 + x + 1
* gepackt (ohne den x^16-Term): 0x39B7
* gepackt (mit dem x^16-Term): 0x139B7
*
* Richtung: MSB-first („normal", nicht-reflektiert).
*
* Tabellen-Konstruktion
* ---------------------
* Jeder Eintrag ist der Rest einer Polynomialdivision:
*
* table[i] = ( i * x^(16 + 8*k) ) mod P(x)
*
* berechnet, indem i in das obere Byte eines 16-Bit-Registers geladen
* wird (crc = i << 8) und 8 + 8*k Left-Shifts durchgeführt werden,
* wobei das Polynomial hinein-XORiert wird, sobald vor einem Shift Bit
* 15 gesetzt ist:
*
* for (n = 0; n < 8 + 8*k; n++)
* crc = (crc & 0x8000) ? ((crc << 1) ^ 0x39B7)
* : (crc << 1);
*
* Tabellen in dieser Datei
* ------------------------
* CRC16_TABLE (k = 0, 8 Shifts):
* table[i] = (i * x^16) mod P.
* Gewöhnliche byte-weise Lookup-Tabelle. Dient dazu, den CRC ein Byte
* nach dem anderen zu aktualisieren:
* crc = (crc << 8) ^ CRC16_TABLE[byte];
*
* CRC16_TABLE2 (k = 3, 32 Shifts):
* table[i] = (i * x^40) mod P.
* Slice-by-4-Tabelle für das niederwertigste Byte einer 4-Byte-Gruppe;
* äquivalent das Ergebnis der vierfachen Anwendung des gewöhnlichen
* Tabellen-Lookups, beginnend bei Byte i (Byte i gefolgt von drei
* Null-Bytes).
*/
#include <stdint.h>
const uint16_t CRC16_TABLE[256] = {
0x0000, 0x39B7, 0x736E, 0x4AD9, 0xE6DC, 0xDF6B, 0x95B2, 0xAC05,
0xF40F, 0xCDB8, 0x8761, 0xBED6, 0x12D3, 0x2B64, 0x61BD, 0x580A,
0xD1A9, 0xE81E, 0xA2C7, 0x9B70, 0x3775, 0x0EC2, 0x441B, 0x7DAC,
0x25A6, 0x1C11, 0x56C8, 0x6F7F, 0xC37A, 0xFACD, 0xB014, 0x89A3,
0x9AE5, 0xA352, 0xE98B, 0xD03C, 0x7C39, 0x458E, 0x0F57, 0x36E0,
0x6EEA, 0x575D, 0x1D84, 0x2433, 0x8836, 0xB181, 0xFB58, 0xC2EF,
0x4B4C, 0x72FB, 0x3822, 0x0195, 0xAD90, 0x9427, 0xDEFE, 0xE749,
0xBF43, 0x86F4, 0xCC2D, 0xF59A, 0x599F, 0x6028, 0x2AF1, 0x1346,
0x0C7D, 0x35CA, 0x7F13, 0x46A4, 0xEAA1, 0xD316, 0x99CF, 0xA078,
0xF872, 0xC1C5, 0x8B1C, 0xB2AB, 0x1EAE, 0x2719, 0x6DC0, 0x5477,
0xDDD4, 0xE463, 0xAEBA, 0x970D, 0x3B08, 0x02BF, 0x4866, 0x71D1,
0x29DB, 0x106C, 0x5AB5, 0x6302, 0xCF07, 0xF6B0, 0xBC69, 0x85DE,
0x9698, 0xAF2F, 0xE5F6, 0xDC41, 0x7044, 0x49F3, 0x032A, 0x3A9D,
0x6297, 0x5B20, 0x11F9, 0x284E, 0x844B, 0xBDFC, 0xF725, 0xCE92,
0x4731, 0x7E86, 0x345F, 0x0DE8, 0xA1ED, 0x985A, 0xD283, 0xEB34,
0xB33E, 0x8A89, 0xC050, 0xF9E7, 0x55E2, 0x6C55, 0x268C, 0x1F3B,
0x18FA, 0x214D, 0x6B94, 0x5223, 0xFE26, 0xC791, 0x8D48, 0xB4FF,
0xECF5, 0xD542, 0x9F9B, 0xA62C, 0x0A29, 0x339E, 0x7947, 0x40F0,
0xC953, 0xF0E4, 0xBA3D, 0x838A, 0x2F8F, 0x1638, 0x5CE1, 0x6556,
0x3D5C, 0x04EB, 0x4E32, 0x7785, 0xDB80, 0xE237, 0xA8EE, 0x9159,
0x821F, 0xBBA8, 0xF171, 0xC8C6, 0x64C3, 0x5D74, 0x17AD, 0x2E1A,
0x7610, 0x4FA7, 0x057E, 0x3CC9, 0x90CC, 0xA97B, 0xE3A2, 0xDA15,
0x53B6, 0x6A01, 0x20D8, 0x196F, 0xB56A, 0x8CDD, 0xC604, 0xFFB3,
0xA7B9, 0x9E0E, 0xD4D7, 0xED60, 0x4165, 0x78D2, 0x320B, 0x0BBC,
0x1487, 0x2D30, 0x67E9, 0x5E5E, 0xF25B, 0xCBEC, 0x8135, 0xB882,
0xE088, 0xD93F, 0x93E6, 0xAA51, 0x0654, 0x3FE3, 0x753A, 0x4C8D,
0xC52E, 0xFC99, 0xB640, 0x8FF7, 0x23F2, 0x1A45, 0x509C, 0x692B,
0x3121, 0x0896, 0x424F, 0x7BF8, 0xD7FD, 0xEE4A, 0xA493, 0x9D24,
0x8E62, 0xB7D5, 0xFD0C, 0xC4BB, 0x68BE, 0x5109, 0x1BD0, 0x2267,
0x7A6D, 0x43DA, 0x0903, 0x30B4, 0x9CB1, 0xA506, 0xEFDF, 0xD668,
0x5FCB, 0x667C, 0x2CA5, 0x1512, 0xB917, 0x80A0, 0xCA79, 0xF3CE,
0xABC4, 0x9273, 0xD8AA, 0xE11D, 0x4D18, 0x74AF, 0x3E76, 0x07C1,
};
const uint16_t CRC16_TABLE2[256] = {
0x0000, 0x7648, 0xEC90, 0x9AD8, 0xE097, 0x96DF, 0x0C07, 0x7A4F,
0xF899, 0x8ED1, 0x1409, 0x6241, 0x180E, 0x6E46, 0xF49E, 0x82D6,
0xC885, 0xBECD, 0x2415, 0x525D, 0x2812, 0x5E5A, 0xC482, 0xB2CA,
0x301C, 0x4654, 0xDC8C, 0xAAC4, 0xD08B, 0xA6C3, 0x3C1B, 0x4A53,
0xA8BD, 0xDEF5, 0x442D, 0x3265, 0x482A, 0x3E62, 0xA4BA, 0xD2F2,
0x5024, 0x266C, 0xBCB4, 0xCAFC, 0xB0B3, 0xC6FB, 0x5C23, 0x2A6B,
0x6038, 0x1670, 0x8CA8, 0xFAE0, 0x80AF, 0xF6E7, 0x6C3F, 0x1A77,
0x98A1, 0xEEE9, 0x7431, 0x0279, 0x7836, 0x0E7E, 0x94A6, 0xE2EE,
0x68CD, 0x1E85, 0x845D, 0xF215, 0x885A, 0xFE12, 0x64CA, 0x1282,
0x9054, 0xE61C, 0x7CC4, 0x0A8C, 0x70C3, 0x068B, 0x9C53, 0xEA1B,
0xA048, 0xD600, 0x4CD8, 0x3A90, 0x40DF, 0x3697, 0xAC4F, 0xDA07,
0x58D1, 0x2E99, 0xB441, 0xC209, 0xB846, 0xCE0E, 0x54D6, 0x229E,
0xC070, 0xB638, 0x2CE0, 0x5AA8, 0x20E7, 0x56AF, 0xCC77, 0xBA3F,
0x38E9, 0x4EA1, 0xD479, 0xA231, 0xD87E, 0xAE36, 0x34EE, 0x42A6,
0x08F5, 0x7EBD, 0xE465, 0x922D, 0xE862, 0x9E2A, 0x04F2, 0x72BA,
0xF06C, 0x8624, 0x1CFC, 0x6AB4, 0x10FB, 0x66B3, 0xFC6B, 0x8A23,
0xD19A, 0xA7D2, 0x3D0A, 0x4B42, 0x310D, 0x4745, 0xDD9D, 0xABD5,
0x2903, 0x5F4B, 0xC593, 0xB3DB, 0xC994, 0xBFDC, 0x2504, 0x534C,
0x191F, 0x6F57, 0xF58F, 0x83C7, 0xF988, 0x8FC0, 0x1518, 0x6350,
0xE186, 0x97CE, 0x0D16, 0x7B5E, 0x0111, 0x7759, 0xED81, 0x9BC9,
0x7927, 0x0F6F, 0x95B7, 0xE3FF, 0x99B0, 0xEFF8, 0x7520, 0x0368,
0x81BE, 0xF7F6, 0x6D2E, 0x1B66, 0x6129, 0x1761, 0x8DB9, 0xFBF1,
0xB1A2, 0xC7EA, 0x5D32, 0x2B7A, 0x5135, 0x277D, 0xBDA5, 0xCBED,
0x493B, 0x3F73, 0xA5AB, 0xD3E3, 0xA9AC, 0xDFE4, 0x453C, 0x3374,
0xB957, 0xCF1F, 0x55C7, 0x238F, 0x59C0, 0x2F88, 0xB550, 0xC318,
0x41CE, 0x3786, 0xAD5E, 0xDB16, 0xA159, 0xD711, 0x4DC9, 0x3B81,
0x71D2, 0x079A, 0x9D42, 0xEB0A, 0x9145, 0xE70D, 0x7DD5, 0x0B9D,
0x894B, 0xFF03, 0x65DB, 0x1393, 0x69DC, 0x1F94, 0x854C, 0xF304,
0x11EA, 0x67A2, 0xFD7A, 0x8B32, 0xF17D, 0x8735, 0x1DED, 0x6BA5,
0xE973, 0x9F3B, 0x05E3, 0x73AB, 0x09E4, 0x7FAC, 0xE574, 0x933C,
0xD96F, 0xAF27, 0x35FF, 0x43B7, 0x39F8, 0x4FB0, 0xD568, 0xA320,
0x21F6, 0x57BE, 0xCD66, 0xBB2E, 0xC161, 0xB729, 0x2DF1, 0x5BB9,
};Zusammenfassung
Die beiden FSoE-CRC-Tabellen sind beide MSB-first-CRC-16-Lookup-Tabellen für das Polynomial $P(x) = $ 0x139B7. Sie unterscheiden sich nur in der Byte-Position $k$ innerhalb eines 4-Byte-Slices:
| Tabelle | $k$ | Formel | Shifts | Zweck |
|---|---|---|---|---|
CRC16_TABLE | 0 | $(i \cdot x^{16}) \bmod P$ | 8 | Gewöhnliche byte-weise CRC-Aktualisierung |
CRC16_TABLE2 | 3 | $(i \cdot x^{40}) \bmod P$ | 32 | Slice-by-4, niederwertigstes Byte |
Das Generator-Programm (GenTables.c) baut beide Tabellen allein aus dem Polynomial auf, und zwar mit einer einzigen table_entry(i, shifts)-Funktion, und gibt die zweite Quelldatei (Tables.c) aus, die die Tabellen samt einem selbstdokumentierenden Header-Kommentar enthält.
Verwandte Beiträge
Überblick & Grundlagen
- Was bedeutet die FSoE-Abkürzung tatsächlich? — was die Abkürzung FSoE bedeutet
- FSoE: Rahmenstruktur anhand von Beispielen erklärt — allgemeiner Safety-PDU-Aufbau
- FSoE: Safety-PDU-Befehlstabelle — die vollständige Liste der FSoE-Befehle
CRC
- FSoE: Welches CRC-Polynom wird verwendet? — das 17-Bit-Polynom des FSoE-CRC
Fehlercodes & Datenformat
- FSoE: Liste aller Kommunikationsfehlercodes — alle FSoE-Kommunikationsfehlercodes
- FSoE: Little-Endian oder Big-Endian? — Byte-Reihenfolge mehrbyteiger Felder