يستخدم بروتوكول FSoE (Functional Safety over EtherCAT) CRC من 16 بت لحماية رسائل السلامة الخاصة به. تعتمد التنفيذات عادةً على جدولي بحث مُحسوبين مسبقًا من 256 مدخلًا لكل منهما، يُسميان تقليديًا CRC16_TABLE وCRC16_TABLE2. يشرح هذا المقال بالضبط كيف تُشتق هذه الجداول من متعددة حدود CRC — دون ثوابت سحرية مُكتوبة يدويًا، فقط متعددة الحدود والخوارزمية.
متعددة الحدود
يُبنى CRC في FSoE على متعددة حدود من 17 بت
$$ P(x) = x^{16} + x^{13} + x^{12} + x^{11} + x^{8} + x^{7} + x^{5} + x^{4} + x^{2} + x + 1 $$والتي، عند تعبئتها كقيمة سداسية عشرية مع حد $x^{16}$ الضمني، تكون 0x139B7. التمثيل بـ 16 بت المُستخدم داخل سجل الإزاحة (متعددة الحدود بدون حد $x^{16}$) هو 0x39B7.
يُعالَج CRC بترتيب MSB-first (الاتجاه “الطبيعي” أو غير المنعكس): يُؤخذ كل بت رسالة من النهاية الأكثر أهمية في سجل العمل، ويُ XOR لمتعددة الحدود كلما كان البت المُزاح من الأعلى هو 1.
ما هو مدخل جدول بحث CRC
يتيح جدول بحث CRC بايتًا ببايت معالجة بايت كامل (8 بتات) ببحث واحد في الجدول بدلًا من 8 إزاحات بت فردية. بالنسبة لـ CRC بترتيب MSB-first بعرض $W = 16$، فإن مدخل قيمة البايت $i$ هو باقي قسمة متعددة الحدود
$$ \text{table}[i] = \left( i \cdot x^{W + 8k} \right) \bmod P(x) $$حيث يختار $k$ أي موضع بايت ضمن مجموعة متعددة البايتات مُخصص لها الجدول:
- العامل $x^{W} = x^{16}$ يُمثل عرض سجل CRC بـ 16 بت (الباقي بعرض 16 بت).
- العامل $x^{8k}$ يُمثل وجود البايت $k$ مواضع بايت أخرى إلى اليمين (أقل أهمية) داخل المجموعة، بحيث تكون هناك $8k$ مواضع بت إضافية من الاختزال مطلوبة.
حسابيًا، يتحقق ذلك بتحميل $i$ في البايت العالي لسجل 16 بت ($\text{crc} = i \ll 8$) ثم إجراء $8 + 8k$ إزاحة يسار، مع XOR لمتعددة الحدود في السجل كلما كان البت 15 (البت على وشك المغادرة من الأعلى) مضبوطًا:
$$ \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} $$هذا هو بالضبط قسمة طويلة لـ $i \cdot x^{8+8k}$ على $P(x)$، مع الاحتفاظ فقط بالباقي بـ 16 بت.
جدولا FSoE
يستخدم تنفيذ FSoE جدولين، يقابلان قيمتين مختلفتين من $k$:
CRC16_TABLE — الجدول العادي بايتًا ببايت ($k = 0$)
$$
\text{CRC16\_TABLE}[i] = \left( i \cdot x^{16} \right) \bmod P(x)
$$يُحسب بـ 8 إزاحات يسار لـ $i \ll 8$. وهو جدول البحث القياسي المُستخدم لتحديث CRC بايتًا واحدًا في كل مرة:
$$ \text{crc} \gets (\text{crc} \ll 8) \oplus \text{CRC16\_TABLE}[\text{byte}] $$CRC16_TABLE2 — جدول Slice-by-4 ($k = 3$)
$$
\text{CRC16\_TABLE2}[i] = \left( i \cdot x^{40} \right) \bmod P(x)
$$يُحسب بـ 32 إزاحة يسار لـ $i \ll 8$ (لأن $8 + 8 \cdot 3 = 32$). وهو جدول slice-by-4 للبايت الأقل أهمية في مجموعة 4 بايتات. وبتعبير مكافئ، هو نتيجة تطبيق بحث CRC16_TABLE العادي أربع مرات بدءًا من البايت $i$ — أي معالجة البايت $i$ متبوعًا بثلاثة بايتات صفرية:
في خطوة تحديث slice-by-4 تستهلك أربعة بايتات دفعة واحدة، يُحدَّث CRC كالتالي:
$$ \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 $$حيث $b_0$ هو بايت الإدخال الأقل أهمية (الذي يحتاج إلى أكبر قدر من الاختزال الإضافي، ومن هنا $k=3$).
برنامج التوليد
يُولِّد برنامج C التالي كلا الجدولين من متعددة الحدود 0x39B7 ويطبعهما ككود C مصدري. الجوهر هو دالة واحدة، table_entry(i, shifts)، التي تُنفذ قسمة متعددة الحدود الطويلة بترتيب MSB-first الموصوفة أعلاه. يُبنى CRC16_TABLE بـ shifts = 8 ($k=0$) وCRC16_TABLE2 بـ shifts = 32 ($k=3$).
/*
* GenTables.c
* ============
*
* برنامج توليد جداول بحث CRC-16 لـ FSoE (Functional Safety over EtherCAT).
*
* يطبع هذا البرنامج، عند تجميعه وتشغيله، كود C مصدري لملف مصدر ثانٍ
* (Tables.c) يحتوي على جدولي بحث بـ 16 بت و256 مدخلًا لكل منهما.
* تُشتق الجداول بشكل بحت من متعددة حدود CRC؛ لا توجد ثوابت مُكتوبة
* يدويًا في المخرجات المُولَّدة.
*
*
* الـ CRC
* --------
*
* CRC في FSoE هو CRC بـ 16 بت يستخدم متعددة حدود من 17 بت
*
* P(x) = x^16 + x^13 + x^12 + x^11 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
*
* والتي في شكلها السداسي العشري المُعبَّأ (مع إزالة حد x^16 الضمني) هي
*
* 0x39B7 (16 bits)
*
* أو، بما في ذلك حد x^16،
*
* 0x139B7 (17 bits).
*
* يُعالَج CRC بترتيب MSB-first (يُسمى أحيانًا الاتجاه "الطبيعي" أو
* "غير المنعكس"): يُؤخذ كل بت رسالة من النهاية الأكثر أهمية في سجل
* العمل ويُ XOR لمتعددة الحدود عندما يكون البت المُزاح هو 1.
*
*
* كيف تُنتج الجداول
* ---------------------------
*
* يتيح جدول بحث CRC معالجة بايت كامل (8 بتات) ببحث واحد في الجدول
* بدلًا من 8 إزاحات بت فردية. بالنسبة لـ CRC بترتيب MSB-first،
* فإن مدخل قيمة البايت `i` هو باقي قسمة متعددة الحدود
*
* table[i] = ( i * x^(16 + 8*k) ) mod P(x)
*
* حيث يختار `k` أي موضع بايت ضمن مجموعة متعددة البايتات مُخصص لها
* الجدول. العامل x^16 يُمثل عرض سجل CRC بـ 16 بت (الباقي بعرض 16 بت)،
* والعامل x^(8*k) يُمثل وجود البايت `k` مواضع بايت أخرى إلى اليمين
* (أقل أهمية) داخل المجموعة، بحيث تكون `8*k` مواضع بت إضافية من
* الاختزال مطلوبة.
*
* حسابيًا يتحقق ذلك بتحميل `i` في البايت العالي لسجل 16 بت
* (crc = i << 8) ثم إجراء `8 + 8*k` إزاحة يسار، مع XOR لمتعددة الحدود
* في السجل كلما كان البت على وشك المغادرة من الأعلى (البت 15) مضبوطًا:
*
* for (n = 0; n < 8 + 8*k; n++)
* crc = (crc & 0x8000) ? ((crc << 1) ^ POLY) : (crc << 1);
*
* الجدولان المُنتجان هنا هما:
*
* CRC16_TABLE (k = 0): table[i] = (i * x^16) mod P -- 8 إزاحات
* هذا هو جدول البحث العادي بايتًا ببايت. يُستخدم لتحديث
* CRC بايتًا واحدًا في كل مرة:
* crc = (crc << 8) ^ CRC16_TABLE[byte];
*
* CRC16_TABLE2 (k = 3): table[i] = (i * x^40) mod P -- 32 إزاحة
* هذا هو جدول slice-by-4 للبايت الأقل أهمية في مجموعة 4 بايتات.
* وبتعبير مكافئ، هو نتيجة تطبيق بحث الجدول العادي أربع مرات
* بدءًا من البايت `i` (أي معالجة البايت `i` متبوعًا بثلاثة بايتات
* صفرية). يُستخدم في خطوة تحديث slice-by-4 التي تستهلك أربعة
* بايتات دفعة واحدة:
* crc ^= (input32 & 0xFF); // دمج
* crc = CRC16_TABLE2[crc & 0xFF] // البايت الأقل أهمية
* ^ CRC16_TABLE [(input32 >> 8) & 0xFF] // ...ثم
* ^ <slice k=1 table>[(input32 >> 16) & 0xFF] // الباقي
* ^ <slice k=2 table>[(input32 >> 24) & 0xFF] // من البايتات.
* (تنفيذ FSoE يتطلب فقط CRC16_TABLE و CRC16_TABLE2؛
* جدولي slice لـ k=1 و k=2 لا يُولَّدان هنا.)
*
* البناء والتشغيل:
* cc -std=c99 -O2 -o GenTables GenTables.c
* ./GenTables > Tables.c
*/
#include <stdint.h>
#include <stdio.h>
/* البتات الـ 16 السفلى من متعددة الحدود 0x139B7 (حد x^16 ضمني). */
#define POLY 0x39B7U
/*
* حساب مدخل واحد في جدول CRC-16 بترتيب MSB-first.
*
* i : قيمة البايت (0..255) المُستخدمة كفهرس للجدول.
* shifts : عدد الإزاحات اليسارية المطلوب تنفيذها = 8 + 8*k، حيث k هو
* موضع البايت ضمن شريحة 4 بايتات (k=0 -> 8 إزاحات، k=3 ->
* 32 إزاحة).
*
* يُوضع البايت في الثمانية العالية لسجل 16 بت ويُزاح يسارًا `shifts`
* مرة. كلما كان البت العلوي (البت 15) مضبوطًا قبل إزاحة، يُ XOR
* لمتعددة الحدود بعدها، وهو بالضبط قسمة طويلة لـ (i * x^(8+shifts))
* على P(x) مع الاحتفاظ فقط بالباقي بـ 16 بت.
*/
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;
}
/*
* ملء جدول من 256 مدخلًا بحساب كل مدخل بشكل مستقل.
* `shifts` يختار أي جدول slice يُبنى (8 لـ k=0، 32 لـ 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);
}
/* طباعة جدول uint16_t من 256 مدخلًا ككود C مصدري، 8 قيم لكل صف. */
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); /* جدول عادي بايتًا ببايت */
build_table(table2, 32); /* slice-by-4، البايت الأقل أهمية */
/*
* إصدار ملف المصدر الثاني (Tables.c). التعليق الترويسي المكتوب
* هنا يوثق بالضبط ما هي الجداول وكيف ترتبط بمتعددة الحدود،
* بحيث يكون Tables.c مكتفٍ ذاتيًا.
*/
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;
}البناء والتشغيل:
cc -std=c99 -O2 -o GenTables GenTables.c
./GenTables > Tables.cالجداول المُولَّدة
مخرجات المولِّد هي ملف C مصدري مكتفٍ ذاتيًا التالي. يطابق كلا الجدولين جداول FSoE المرجعية بالضبط — كل واحد من الـ 256 مدخلًا في كل جدول.
/*
* Tables.c (auto-generated by GenTables.c -- do not edit)
* =======================================================
*
* جدولا بحث CRC بـ 16 بت و256 مدخلًا لكل منهما لـ
* FSoE (Functional Safety over EtherCAT) CRC-16.
*
* متعددة الحدود
* -----------
* P(x) = x^16 + x^13 + x^12 + x^11 + x^8 + x^7 + x^5
* + x^4 + x^2 + x + 1
* packed (without the x^16 term): 0x39B7
* packed (with the x^16 term): 0x139B7
*
* الاتجاه: MSB-first ("normal", non-reflected).
*
* بناء الجدول
* ------------------
* كل مدخل هو باقي قسمة متعددة حدود:
*
* table[i] = ( i * x^(16 + 8*k) ) mod P(x)
*
* يُحسب بتحميل i في البايت العالي لسجل 16 بت
* (crc = i << 8) وإجراء 8 + 8*k إزاحة يسار،
* مع XOR لمتعددة الحدود كلما كان البت 15 مضبوطًا
* قبل إزاحة:
*
* for (n = 0; n < 8 + 8*k; n++)
* crc = (crc & 0x8000) ? ((crc << 1) ^ 0x39B7)
* : (crc << 1);
*
* الجداول في هذا الملف
* -------------------
* CRC16_TABLE (k = 0, 8 shifts):
* table[i] = (i * x^16) mod P.
* جدول بحث عادي بايتًا ببايت. يُستخدم لتحديث
* CRC بايتًا واحدًا في كل مرة:
* crc = (crc << 8) ^ CRC16_TABLE[byte];
*
* CRC16_TABLE2 (k = 3, 32 shifts):
* table[i] = (i * x^40) mod P.
* جدول slice-by-4 للبايت الأقل أهمية في مجموعة
* 4 بايتات؛ وبتعبير مكافئ هو نتيجة تطبيق بحث الجدول
* العادي أربع مرات بدءًا من البايت i
* (البايت i متبوعًا بثلاثة بايتات صفرية).
*/
#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,
};الخلاصة
كلا جدولي CRC في FSoE هما جدولا بحث CRC-16 بترتيب MSB-first لمتعددة الحدود $P(x) = $ 0x139B7. يختلفان فقط في موضع البايت $k$ ضمن شريحة 4 بايتات:
| الجدول | $k$ | الصيغة | الإزاحات | الغرض |
|---|---|---|---|---|
CRC16_TABLE | 0 | $(i \cdot x^{16}) \bmod P$ | 8 | تحديث CRC عادي بايتًا ببايت |
CRC16_TABLE2 | 3 | $(i \cdot x^{40}) \bmod P$ | 32 | Slice-by-4، البايت الأقل أهمية |
يبني برنامج المولِّد (GenTables.c) كلا الجدولين من متعددة الحدود وحدها باستخدام دالة واحدة table_entry(i, shifts)، ويُصدر ملف المصدر الثاني (Tables.c) الحاوي للجداول مع تعليق ترويسة موثِّق ذاتيًا.