1. 程式人生 > >內建CRC於hex程式中的方法

內建CRC於hex程式中的方法

【摘要】

為了讓MCU程式顯示自身的CRC值,需要將其內建到程式中。但是,通常情況下,用計算好的CRC值,代替程式中原有的預設值之後,會導致程式發生變動,進而引發CRC值的變動。最終,新程式顯示的值,是舊程式的CRC值。

1、問題原因分析

首先要說明一下,為什麼要內建CRC值到程式中。
  把程式分成兩個hex檔案,一個是普通的程式,另一個單獨使用Flash的一頁,並將CRC值放置在其中,每次下載兩個hex檔案,不行嗎?這方法本身是可行的,但是,為了顯示CRC值,而使用Flash的一頁,太浪費了。而且,軟體入庫時,入2個hex檔案,生產部門下載時,也要下載2個hex檔案,太麻煩了。
那麼,通過上位機把CRC值傳給MCU,並儲存到EEPROM之類的地方,不行嗎?這方法不但麻煩,而且不是所有的板子都帶外擴儲存器。
既然上述方法那麼麻煩,那麼讓MCU自己去計算CRC值,不行嗎?這方法的問題就更多了。我們的大前提,要求MCU計算出的CRC值,與通過校驗碼軟體算出的CRC值,二者必須一致。
是,二者有資訊不對等的弊病。MCU無法得到hex檔案,因此無法知道程式的準確大小,當然也無法知道在Flash內的內容中,哪些是正常的程式,哪些是在hex檔案中不存在而自動填充的0xFF(通稱窟窿),哪些是使用J-Link下載時未擦除而殘餘的其他程式。再來看校驗碼軟體,軟體讀取hex檔案,通常可以知道Flash區的起始地址,但一般無法知道Flash的容量。讓使用者敲Flash容量?既然MCU中可能會存在未擦乾淨的其他程式,那麼校驗碼軟體就算知道了Flash容量,又有什麼意義呢。所以,讓MCU自己去計算CRC值,是不現實的。

  所以,最後還是選擇內建CRC值到程式中,問題在於修正程式使CRC值不變。

2、修正值

  之前寫的典型案例《指定CRC反構資料》,給出了一種方法,通過修改檔案中任意位置的連續4位元組,可以得到任意指定的CRC32值。我們的校驗碼軟體,是在CRC32的基礎上,改造為適合於hex檔案的演算法,因此其核心仍然是CRC32,可以通過類似的方法進行修正。這種方法,需要新建一個工具,或者修改校驗碼軟體,由軟體處理最終的hex檔案,然後把CRC值和修正值,全部傳回原始檔,再重新編譯。但是問題來了,CRC值放在哪個地址?修正值放在哪個地址?讓軟體工程師自己敲?總而言之,這不是最好的辦法。
雖然IAR自帶了一個CRC計算的工具,並能夠嵌入到目標程式中的指定位置,但那並不能生成與我們的校驗碼軟體一致的CRC值,因此我們至少必須把CRC值傳回原始檔,再重新編譯。問題在於,如何讓原始碼自動計算修正值。實際上,我們不必遍歷完整的hex檔案。只要找到CRC值和修正值的對應關係,並且二者挨在一起,那麼不論放在什麼地址,都不會影響最終的CRC值。這個方法是目前為止的最好辦法。
我們拿十進位制的數學餘數來展示原理,二進位制的邏輯餘數的原理是類似的。
舉例:數字12300004560000789對61的餘數,結果是23。我們可以把“23”這兩個數,替換到數字中4個0的位置,並在之後填充修正值。修正值是多少呢?算一下2300對61的餘數,結果是43,與61的差值為18,因此修正值就是18。把餘數和修正值連在一起,就是2318,這個數不論填在前面4個0的位置(1232318
4560000789),還是填在後面4個0的位置(12300004562318789),甚至全填(12323184562318789),這幾個數對61的餘數仍然是23。原因很簡單,2318對61的餘數是零,因此不論2318後面添幾個零,加到原數上都不影響其餘數。

3、CRC32修正值

  CRC32演算法採用的多項式(POLY)為0x04C11DB7,顛倒之後為0xEDB88320。為什麼要顛倒?因為這個CRC32演算法,在處理位元組中的各個位的時候,採用的是從低位開始向高位依次處理的方法,與我們直觀的從高到低的處理方法相反,所以把多項式顛倒過來。位的序號也要顛倒過來看。顛倒之後,以二進位制形式表示,就是:

0

7

8

15

16

23

24

31

1

1

1

0

1

1

0

1

1

0

1

1

1

0

0

0

1

0

0

0

0

0

1

1

0

0

1

0

0

0

0

0

(畫表格真難)

同樣,我們把CRC域,以及修正值(以下稱為TAIL)域也按位展開,並添零:

0

7

8

15

16

23

24

31

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

7

8

15

16

23

24

31

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

  首先處理CRC域的第0位:當CRC值的第0位為0,不必處理;當CRC值的第0位為1,在把這個1替換到CRC域的同時,為了消除其影響,需要把CRC域的第1~31位,以及TAIL域的第0位,這共計32位,按順序和顛倒多項式的各個位進行“異或”。
  接下來處理CRC域的第1位:當CRC域的第1位和CRC值的第1位相同,不必處理;當CRC域的第1位和CRC值的第1位不同,需要把CRC域的第1位替換為CRC值的第1位,與此同時,為了消除其影響,需要把CRC域的第2~31位,以及TAIL域的第0~1位,這共計32位,按順序和顛倒多項式的各個位進行“異或”。
  以此類推……
  最後處理CRC域的第31位:當CRC域的第31位和CRC值的第31位相同,不必處理;當CRC域的第31位和CRC值的第31位不同,需要把CRC域的第31位替換為CRC值的第1位,與此同時,為了消除其影響,需要把TAIL域的第0~31位,按順序和顛倒多項式的各個位進行“異或”。
  把CRC域的32位和TAIL域的32位,拼成8個位元組,這8個位元組的特點,是對多項式的餘數為零。這樣一來,把這8個位元組替換到程式中任意8個連續0x00位元組的位置,都不會影響程式最終的CRC值。

4、自動計算

  知道了原理,接下來就是用程式碼實現自動計算了。計算過程中涉及的量,全部都是常量,那麼我們要用什麼常量呢?
首先是CRC值,這個用#define巨集定義即可。之所以不用const變數,是因為在C語言中,不承認const變數為常量。之所以不用enum,是因為enum是整形數,而我們需要無符號整形來表示CRC32的值。因此用#define最合適。
#define self_crc    0xFFFFFFFF
接下來是用迭代方法,一步一步計算出TAIL的值。既然是迭代,那麼用巨集定義很容易導致迭代結果異常複雜,當然用const變數也是不行的。因此這裡用enum常量。這裡,用乘法來代替分支,因為寫起來比分支語句方便。
#define POLY    0xEDB88320U

enum { x00 = 0 };
enum { x01 = ((uint32_t)x00 >> 1) ^ (POLY * (1 & (x00 ^ (self_crc >>  0)))) };
enum { x02 = ((uint32_t)x01 >> 1) ^ (POLY * (1 & (x01 ^ (self_crc >>  1)))) };
enum { x03 = ((uint32_t)x02 >> 1) ^ (POLY * (1 & (x02 ^ (self_crc >>  2)))) };
enum { x04 = ((uint32_t)x03 >> 1) ^ (POLY * (1 & (x03 ^ (self_crc >>  3)))) };
enum { x05 = ((uint32_t)x04 >> 1) ^ (POLY * (1 & (x04 ^ (self_crc >>  4)))) };
enum { x06 = ((uint32_t)x05 >> 1) ^ (POLY * (1 & (x05 ^ (self_crc >>  5)))) };
enum { x07 = ((uint32_t)x06 >> 1) ^ (POLY * (1 & (x06 ^ (self_crc >>  6)))) };

enum { x10 = ((uint32_t)x07 >> 1) ^ (POLY * (1 & (x07 ^ (self_crc >>  7)))) };
enum { x11 = ((uint32_t)x10 >> 1) ^ (POLY * (1 & (x10 ^ (self_crc >>  8)))) };
enum { x12 = ((uint32_t)x11 >> 1) ^ (POLY * (1 & (x11 ^ (self_crc >>  9)))) };
enum { x13 = ((uint32_t)x12 >> 1) ^ (POLY * (1 & (x12 ^ (self_crc >> 10)))) };
enum { x14 = ((uint32_t)x13 >> 1) ^ (POLY * (1 & (x13 ^ (self_crc >> 11)))) };
enum { x15 = ((uint32_t)x14 >> 1) ^ (POLY * (1 & (x14 ^ (self_crc >> 12)))) };
enum { x16 = ((uint32_t)x15 >> 1) ^ (POLY * (1 & (x15 ^ (self_crc >> 13)))) };
enum { x17 = ((uint32_t)x16 >> 1) ^ (POLY * (1 & (x16 ^ (self_crc >> 14)))) };

enum { x20 = ((uint32_t)x17 >> 1) ^ (POLY * (1 & (x17 ^ (self_crc >> 15)))) };
enum { x21 = ((uint32_t)x20 >> 1) ^ (POLY * (1 & (x20 ^ (self_crc >> 16)))) };
enum { x22 = ((uint32_t)x21 >> 1) ^ (POLY * (1 & (x21 ^ (self_crc >> 17)))) };
enum { x23 = ((uint32_t)x22 >> 1) ^ (POLY * (1 & (x22 ^ (self_crc >> 18)))) };
enum { x24 = ((uint32_t)x23 >> 1) ^ (POLY * (1 & (x23 ^ (self_crc >> 19)))) };
enum { x25 = ((uint32_t)x24 >> 1) ^ (POLY * (1 & (x24 ^ (self_crc >> 20)))) };
enum { x26 = ((uint32_t)x25 >> 1) ^ (POLY * (1 & (x25 ^ (self_crc >> 21)))) };
enum { x27 = ((uint32_t)x26 >> 1) ^ (POLY * (1 & (x26 ^ (self_crc >> 22)))) };

enum { x30 = ((uint32_t)x27 >> 1) ^ (POLY * (1 & (x27 ^ (self_crc >> 23)))) };
enum { x31 = ((uint32_t)x30 >> 1) ^ (POLY * (1 & (x30 ^ (self_crc >> 24)))) };
enum { x32 = ((uint32_t)x31 >> 1) ^ (POLY * (1 & (x31 ^ (self_crc >> 25)))) };
enum { x33 = ((uint32_t)x32 >> 1) ^ (POLY * (1 & (x32 ^ (self_crc >> 26)))) };
enum { x34 = ((uint32_t)x33 >> 1) ^ (POLY * (1 & (x33 ^ (self_crc >> 27)))) };
enum { x35 = ((uint32_t)x34 >> 1) ^ (POLY * (1 & (x34 ^ (self_crc >> 28)))) };
enum { x36 = ((uint32_t)x35 >> 1) ^ (POLY * (1 & (x35 ^ (self_crc >> 29)))) };
enum { x37 = ((uint32_t)x36 >> 1) ^ (POLY * (1 & (x36 ^ (self_crc >> 30)))) };

enum { x40 = ((uint32_t)x37 >> 1) ^ (POLY * (1 & (x37 ^ (self_crc >> 31)))) };

  注意,對列舉常量進行右移操作之前,需要強制轉換為無符號整形數。這些雖然寫起來很囉嗦,但是現在的主流編輯器都有“列塊編輯模式”的,寫起來也很容易。
  最後,就是把CRC值和TAIL值,固定到Flash的某個位置。
__root const uint32_t SELF_CRC @ 0x08002000 = self_crc;
__root const static uint32_t TAIL_0x2000 @ 0x08002004 = x40;

  其中,“__root”是IAR裡面的關鍵字,用來保證其修飾的變數一定會包含到目標程式中,不會因沒被使用而“被優化”消失掉。SELF_CRC是需要給其他模組用的,因此不能加static。TAIL_0x2000的值不必給其他模組使用,因此加上static修飾。最後的@ 0x08002000和@ 0x08002004,是為了把這兩個變數固定在Flash的指定地址處,防止其位置“亂飄”。最後在標頭檔案中,用extern定義一個SELF_CRC的外部符號。
extern const uint32_t SELF_CRC;

5、功能擴充套件

  使用本方法,不但可以實現CRC的內建,還可以實現其他易變常量的內建,比如編譯日期時間。__DATE__和__TIME__就屬於易變常量,當程式要顯示自身的編譯時間時,這兩個常量的加入,也會導致CRC的變動。
  其中,前者的格式看起來是"Feb 12 1996",後者是"23:59:01"。為了便於顯示,我們把這兩個量的各個子字元取出來:
enum { YEAR_0 = (uint8_t)(__DATE__[ 7] - '0') };
enum { YEAR_1 = (uint8_t)(__DATE__[ 8] - '0') };
enum { YEAR_2 = (uint8_t)(__DATE__[ 9] - '0') };
enum { YEAR_3 = (uint8_t)(__DATE__[10] - '0') };
enum { MON_0  = (char)    __DATE__[ 0]        };
enum { MON_1  = (char)    __DATE__[ 1]        };
enum { MON_2  = (char)    __DATE__[ 2]        };
enum { DAY_0  = (char)    __DATE__[ 4]        };
enum { DAY_1  = (char)    __DATE__[ 5]        };

enum { HOUR_0 = (uint8_t)(__TIME__[ 0] - '0') };
enum { HOUR_1 = (uint8_t)(__TIME__[ 1] - '0') };
enum { MIN_0  = (uint8_t)(__TIME__[ 3] - '0') };
enum { MIN_1  = (uint8_t)(__TIME__[ 4] - '0') };
enum { SEC_0  = (uint8_t)(__TIME__[ 6] - '0') };
enum { SEC_1  = (uint8_t)(__TIME__[ 7] - '0') };

  其中,“年”、“時分秒”,直接取成數字即可。“月”是以英文表示的,所以取成字元本身。“日”雖然是數字的,但是1日~9日的值,會用空格填充十位的位置,因此也要取成字元。
  “月日”轉換成數字的方法如下:
enum { MON =
    (MON_0 == 'J' && MON_1 == 'a' && MON_2 == 'n') ?  1 :
    (MON_0 == 'F' && MON_1 == 'e' && MON_2 == 'b') ?  2 :
    (MON_0 == 'M' && MON_1 == 'a' && MON_2 == 'r') ?  3 :
    (MON_0 == 'A' && MON_1 == 'p' && MON_2 == 'r') ?  4 :
    (MON_0 == 'M' && MON_1 == 'a' && MON_2 == 'y') ?  5 :
    (MON_0 == 'J' && MON_1 == 'u' && MON_2 == 'n') ?  6 :
    (MON_0 == 'J' && MON_1 == 'u' && MON_2 == 'l') ?  7 :
    (MON_0 == 'A' && MON_1 == 'u' && MON_2 == 'g') ?  8 :
    (MON_0 == 'S' && MON_1 == 'e' && MON_2 == 'p') ?  9 :
    (MON_0 == 'O' && MON_1 == 'c' && MON_2 == 't') ? 10 :
    (MON_0 == 'N' && MON_1 == 'o' && MON_2 == 'v') ? 11 :
    (MON_0 == 'D' && MON_1 == 'e' && MON_2 == 'c') ? 12 :
    0xFF
};

enum { DAY =
    (DAY_0 == ' ' && DAY_1 >= '1' && DAY_1 <= '9')
    ? DAY_1 - '0'
    : (DAY_0 >= '1' && DAY_0 <= '3' && DAY_1 >= '0' && DAY_1 <= '9')
    ? 10 * (DAY_0 - '0') + (DAY_1 - '0')
    : 0xFF
};
  看起來很麻煩,因為全是編譯器常量所以沒有問題。“年”、“時分秒”則可以非常簡單的轉換成數字。用壓縮BCD碼來儲存“年月日時分秒”,用到7個位元組,那麼為了對齊到4位元組倍數,可以再填一個“星期”,這樣就是8個位元組無浪費了。
  注意,一個TAIL是可以對應任意位元組的易變常量的,不只限於CRC的4個位元組。我們把CRC、“年月日時分秒”串在一起,用同樣的方法計算出新的TAIL,全部連起來一共是16個位元組,考慮到IAR輸出的hex檔案,每行有16位元組資料,所以這些資料正好在hex檔案的同一行。

6、效果評價

  將這兩個檔案新增到任意一個IAR工程中,先進行第一遍編譯。注意,如果Flash的指定地址被其他功能佔用,那麼只需換個地址即可。
  得到hex檔案後,用校驗碼軟體算出CRC,用這個CRC的值替換掉self_crc的巨集定義,然後進行第二遍編譯。這樣,新產生的hex檔案就是內建了CRC的程式。
  一般情況下,開發過程中不必如此進行二次編譯,只有在完成之後,或者想要看到CRC的時候,進行二次編譯即可。

7、完整程式碼

“self_crc.h”:

#ifndef SELF_CRC_H
#define SELF_CRC_H

#ifdef __cplusplus
extern"C"{
#endif

extern const uint32_t SELF_CRC;

#ifdef __cplusplus
}
#endif

#endif // SELF_CRC_H

self_crc.c”:
#include <stdint.h>
#include "self_crc.h"

#define self_crc    0xFFFFFFFF

//////////////////////////////////////////////////////////////////////////////

__root const uint32_t SELF_CRC @ 0x08002000 = self_crc;

// `__DATE__'
//      This macro expands to a string constant that describes the date on
//      which the preprocessor is being run.  The string constant contains
//      eleven characters and looks like `"Feb 12 1996"'.  If the day of
//      the month is less than 10, it is padded with a space on the left.
//
//      If GCC cannot determine the current date, it will emit a warning
//      message (once per compilation) and `__DATE__' will expand to
//      `"??? ?? ????"'.
enum { YEAR_0 = (uint8_t)(__DATE__[ 7] - '0') };
enum { YEAR_1 = (uint8_t)(__DATE__[ 8] - '0') };
enum { YEAR_2 = (uint8_t)(__DATE__[ 9] - '0') };
enum { YEAR_3 = (uint8_t)(__DATE__[10] - '0') };
enum { MON_0  = (char)    __DATE__[ 0]        };
enum { MON_1  = (char)    __DATE__[ 1]        };
enum { MON_2  = (char)    __DATE__[ 2]        };
enum { DAY_0  = (char)    __DATE__[ 4]        };
enum { DAY_1  = (char)    __DATE__[ 5]        };

// `__TIME__'
//      This macro expands to a string constant that describes the time at
//      which the preprocessor is being run.  The string constant contains
//      eight characters and looks like `"23:59:01"'.
//
//      If GCC cannot determine the current time, it will emit a warning
//      message (once per compilation) and `__TIME__' will expand to
//      `"??:??:??"'.
enum { HOUR_0 = (uint8_t)(__TIME__[ 0] - '0') };
enum { HOUR_1 = (uint8_t)(__TIME__[ 1] - '0') };
enum { MIN_0  = (uint8_t)(__TIME__[ 3] - '0') };
enum { MIN_1  = (uint8_t)(__TIME__[ 4] - '0') };
enum { SEC_0  = (uint8_t)(__TIME__[ 6] - '0') };
enum { SEC_1  = (uint8_t)(__TIME__[ 7] - '0') };

enum { YEAR =
    (YEAR_0 < 10 && YEAR_1 < 10 && YEAR_2 < 10 && YEAR_3 < 10)
    ? 1000 * YEAR_0 + 100 * YEAR_1 + 10 * YEAR_2 + YEAR_3
    : 0xFFFF
};
enum { year_hi =
    (YEAR_0 < 10 && YEAR_1 < 10 && YEAR_2 < 10 && YEAR_3 < 10)
    ? 0x10 * YEAR_0 + YEAR_1
    : 0xFF
};
enum { year_lo =
    (YEAR_0 < 10 && YEAR_1 < 10 && YEAR_2 < 10 && YEAR_3 < 10)
    ? 0x10 * YEAR_2 + YEAR_3
    : 0xFF
};

// JanFebMarAprMayJunJulAugSepOctNovDec
enum { MON =
    (MON_0 == 'J' && MON_1 == 'a' && MON_2 == 'n') ?  1 :
    (MON_0 == 'F' && MON_1 == 'e' && MON_2 == 'b') ?  2 :
    (MON_0 == 'M' && MON_1 == 'a' && MON_2 == 'r') ?  3 :
    (MON_0 == 'A' && MON_1 == 'p' && MON_2 == 'r') ?  4 :
    (MON_0 == 'M' && MON_1 == 'a' && MON_2 == 'y') ?  5 :
    (MON_0 == 'J' && MON_1 == 'u' && MON_2 == 'n') ?  6 :
    (MON_0 == 'J' && MON_1 == 'u' && MON_2 == 'l') ?  7 :
    (MON_0 == 'A' && MON_1 == 'u' && MON_2 == 'g') ?  8 :
    (MON_0 == 'S' && MON_1 == 'e' && MON_2 == 'p') ?  9 :
    (MON_0 == 'O' && MON_1 == 'c' && MON_2 == 't') ? 10 :
    (MON_0 == 'N' && MON_1 == 'o' && MON_2 == 'v') ? 11 :
    (MON_0 == 'D' && MON_1 == 'e' && MON_2 == 'c') ? 12 :
    0xFF
};
enum { mon_bcd =
    (MON <  10) ? MON :
    (MON == 10) ? 0x10 :
    (MON == 11) ? 0x11 :
    (MON == 12) ? 0x12 :
    0xFF
};

enum { DAY =
    (DAY_0 == ' ' && DAY_1 >= '1' && DAY_1 <= '9')
    ? DAY_1 - '0'
    : (DAY_0 >= '1' && DAY_0 <= '3' && DAY_1 >= '0' && DAY_1 <= '9')
    ? 10 * (DAY_0 - '0') + (DAY_1 - '0')
    : 0xFF
};
enum { day_bcd =
    (DAY_0 == ' ' && DAY_1 >= '1' && DAY_1 <= '9')
    ? DAY_1 - '0'
    : (DAY_0 >= '1' && DAY_0 <= '3' && DAY_1 >= '0' && DAY_1 <= '9')
    ? 0x10 * (DAY_0 - '0') + (DAY_1 - '0')
    : 0xFF
};

enum { HOUR = HOUR_0 < 3 && HOUR_1 < 10 ? 10 * HOUR_0 + HOUR_1 : 0xFF };
enum { hour_bcd =
    HOUR_0 < 3 && HOUR_1 < 10 ? 0x10 * HOUR_0 + HOUR_1 : 0xFF
};

enum { MIN = MIN_0 < 6 && MIN_1 < 10 ? 10 * MIN_0 + MIN_1 : 0xFF };
enum { min_bcd =
    MIN_0 < 6 && MIN_1 < 10 ? 0x10 * MIN_0 + MIN_1 : 0xFF
};

enum { SEC = SEC_0 < 6 && SEC_1 < 10 ? 10 * SEC_0 + SEC_1 : 0xFF };
enum { sec_bcd =
    SEC_0 < 6 && SEC_1 < 10 ? 0x10 * SEC_0 + SEC_1 : 0xFF
};

enum { LEAP = (YEAR % 4 == 0 && YEAR % 100 != 0 || YEAR % 400 == 0) ? 1 : 0 };

enum { DAYS =
    MON ==  1 ?   0 :
    MON ==  2 ?  31 :
    MON ==  3 ?  59 :
    MON ==  4 ?  90 :
    MON ==  5 ? 120 :
    MON ==  6 ? 151 :
    MON ==  7 ? 181 :
    MON ==  8 ? 212 :
    MON ==  9 ? 243 :
    MON == 10 ? 273 :
    MON == 11 ? 304 :
    MON == 12 ? 334 :
    MON == 13 ? 365 :
    0
};

enum { LEAP_DAYS =
    MON ==  1 ?   0 :
    MON ==  2 ?  31 :
    MON ==  3 ?  60 :
    MON ==  4 ?  91 :
    MON ==  5 ? 121 :
    MON ==  6 ? 152 :
    MON ==  7 ? 182 :
    MON ==  8 ? 213 :
    MON ==  9 ? 244 :
    MON == 10 ? 274 :
    MON == 11 ? 305 :
    MON == 12 ? 335 :
    MON == 13 ? 366 :
    0
};

enum { WDAY =
    ( (YEAR - 1) * 365 + (YEAR - 1) / 4 - (YEAR - 1) / 100 + (YEAR - 1) / 400
    + (LEAP ? (int)LEAP_DAYS : (int)DAYS) + DAY) % 7
};
enum { wday_bcd = 0xD0 | WDAY };

__root const static uint8_t YEAR_HI  @ 0x08002004 = year_hi;
__root const static uint8_t YEAR_LO  @ 0x08002005 = year_lo;
__root const static uint8_t MON_BCD  @ 0x08002006 = mon_bcd;
__root const static uint8_t DAY_BCD  @ 0x08002007 = day_bcd;
__root const static uint8_t WDAY_BCD @ 0x08002008 = wday_bcd;
__root const static uint8_t HOUR_BCD @ 0x08002009 = hour_bcd;
__root const static uint8_t MIN_BCD  @ 0x0800200A = min_bcd;
__root const static uint8_t SEC_BCD  @ 0x0800200B = sec_bcd;

#define POLY    0xEDB88320U

enum { x00 = 0 };
enum { x01 = ((uint32_t)x00 >> 1) ^ (POLY * (1 & (x00 ^ (self_crc >>  0)))) };
enum { x02 = ((uint32_t)x01 >> 1) ^ (POLY * (1 & (x01 ^ (self_crc >>  1)))) };
enum { x03 = ((uint32_t)x02 >> 1) ^ (POLY * (1 & (x02 ^ (self_crc >>  2)))) };
enum { x04 = ((uint32_t)x03 >> 1) ^ (POLY * (1 & (x03 ^ (self_crc >>  3)))) };
enum { x05 = ((uint32_t)x04 >> 1) ^ (POLY * (1 & (x04 ^ (self_crc >>  4)))) };
enum { x06 = ((uint32_t)x05 >> 1) ^ (POLY * (1 & (x05 ^ (self_crc >>  5)))) };
enum { x07 = ((uint32_t)x06 >> 1) ^ (POLY * (1 & (x06 ^ (self_crc >>  6)))) };

enum { x10 = ((uint32_t)x07 >> 1) ^ (POLY * (1 & (x07 ^ (self_crc >>  7)))) };
enum { x11 = ((uint32_t)x10 >> 1) ^ (POLY * (1 & (x10 ^ (self_crc >>  8)))) };
enum { x12 = ((uint32_t)x11 >> 1) ^ (POLY * (1 & (x11 ^ (self_crc >>  9)))) };
enum { x13 = ((uint32_t)x12 >> 1) ^ (POLY * (1 & (x12 ^ (self_crc >> 10)))) };
enum { x14 = ((uint32_t)x13 >> 1) ^ (POLY * (1 & (x13 ^ (self_crc >> 11)))) };
enum { x15 = ((uint32_t)x14 >> 1) ^ (POLY * (1 & (x14 ^ (self_crc >> 12)))) };
enum { x16 = ((uint32_t)x15 >> 1) ^ (POLY * (1 & (x15 ^ (self_crc >> 13)))) };
enum { x17 = ((uint32_t)x16 >> 1) ^ (POLY * (1 & (x16 ^ (self_crc >> 14)))) };

enum { x20 = ((uint32_t)x17 >> 1) ^ (POLY * (1 & (x17 ^ (self_crc >> 15)))) };
enum { x21 = ((uint32_t)x20 >> 1) ^ (POLY * (1 & (x20 ^ (self_crc >> 16)))) };
enum { x22 = ((uint32_t)x21 >> 1) ^ (POLY * (1 & (x21 ^ (self_crc >> 17)))) };
enum { x23 = ((uint32_t)x22 >> 1) ^ (POLY * (1 & (x22 ^ (self_crc >> 18)))) };
enum { x24 = ((uint32_t)x23 >> 1) ^ (POLY * (1 & (x23 ^ (self_crc >> 19)))) };
enum { x25 = ((uint32_t)x24 >> 1) ^ (POLY * (1 & (x24 ^ (self_crc >> 20)))) };
enum { x26 = ((uint32_t)x25 >> 1) ^ (POLY * (1 & (x25 ^ (self_crc >> 21)))) };
enum { x27 = ((uint32_t)x26 >> 1) ^ (POLY * (1 & (x26 ^ (self_crc >> 22)))) };

enum { x30 = ((uint32_t)x27 >> 1) ^ (POLY * (1 & (x27 ^ (self_crc >> 23)))) };
enum { x31 = ((uint32_t)x30 >> 1) ^ (POLY * (1 & (x30 ^ (self_crc >> 24)))) };
enum { x32 = ((uint32_t)x31 >> 1) ^ (POLY * (1 & (x31 ^ (self_crc >> 25)))) };
enum { x33 = ((uint32_t)x32 >> 1) ^ (POLY * (1 & (x32 ^ (self_crc >> 26)))) };
enum { x34 = ((uint32_t)x33 >> 1) ^ (POLY * (1 & (x33 ^ (self_crc >> 27)))) };
enum { x35 = ((uint32_t)x34 >> 1) ^ (POLY * (1 & (x34 ^ (self_crc >> 28)))) };
enum { x36 = ((uint32_t)x35 >> 1) ^ (POLY * (1 & (x35 ^ (self_crc >> 29)))) };
enum { x37 = ((uint32_t)x36 >> 1) ^ (POLY * (1 & (x36 ^ (self_crc >> 30)))) };

enum { x40 = ((uint32_t)x37 >> 1) ^ (POLY * (1 & (x37 ^ (self_crc >> 31)))) };
enum { x41 = ((uint32_t)x40 >> 1) ^ (POLY * (1 & (x40 ^ (year_hi  >>  0)))) };
enum { x42 = ((uint32_t)x41 >> 1) ^ (POLY * (1 & (x41 ^ (year_hi  >>  1)))) };
enum { x43 = ((uint32_t)x42 >> 1) ^ (POLY * (1 & (x42 ^ (year_hi  >>  2)))) };
enum { x44 = ((uint32_t)x43 >> 1) ^ (POLY * (1 & (x43 ^ (year_hi  >>  3)))) };
enum { x45 = ((uint32_t)x44 >> 1) ^ (POLY * (1 & (x44 ^ (year_hi  >>  4)))) };
enum { x46 = ((uint32_t)x45 >> 1) ^ (POLY * (1 & (x45 ^ (year_hi  >>  5)))) };
enum { x47 = ((uint32_t)x46 >> 1) ^ (POLY * (1 & (x46 ^ (year_hi  >>  6)))) };

enum { x50 = ((uint32_t)x47 >> 1) ^ (POLY * (1 & (x47 ^ (year_hi  >>  7)))) };
enum { x51 = ((uint32_t)x50 >> 1) ^ (POLY * (1 & (x50 ^ (year_lo  >>  0)))) };
enum { x52 = ((uint32_t)x51 >> 1) ^ (POLY * (1 & (x51 ^ (year_lo  >>  1)))) };
enum { x53 = ((uint32_t)x52 >> 1) ^ (POLY * (1 & (x52 ^ (year_lo  >>  2)))) };
enum { x54 = ((uint32_t)x53 >> 1) ^ (POLY * (1 & (x53 ^ (year_lo  >>  3)))) };
enum { x55 = ((uint32_t)x54 >> 1) ^ (POLY * (1 & (x54 ^ (year_lo  >>  4)))) };
enum { x56 = ((uint32_t)x55 >> 1) ^ (POLY * (1 & (x55 ^ (year_lo  >>  5)))) };
enum { x57 = ((uint32_t)x56 >> 1) ^ (POLY * (1 & (x56 ^ (year_lo  >>  6)))) };

enum { x60 = ((uint32_t)x57 >> 1) ^ (POLY * (1 & (x57 ^ (year_lo  >>  7)))) };
enum { x61 = ((uint32_t)x60 >> 1) ^ (POLY * (1 & (x60 ^ (mon_bcd  >>  0)))) };
enum { x62 = ((uint32_t)x61 >> 1) ^ (POLY * (1 & (x61 ^ (mon_bcd  >>  1)))) };
enum { x63 = ((uint32_t)x62 >> 1) ^ (POLY * (1 & (x62 ^ (mon_bcd  >>  2)))) };
enum { x64 = ((uint32_t)x63 >> 1) ^ (POLY * (1 & (x63 ^ (mon_bcd  >>  3)))) };
enum { x65 = ((uint32_t)x64 >> 1) ^ (POLY * (1 & (x64 ^ (mon_bcd  >>  4)))) };
enum { x66 = ((uint32_t)x65 >> 1) ^ (POLY * (1 & (x65 ^ (mon_bcd  >>  5)))) };
enum { x67 = ((uint32_t)x66 >> 1) ^ (POLY * (1 & (x66 ^ (mon_bcd  >>  6)))) };

enum { x70 = ((uint32_t)x67 >> 1) ^ (POLY * (1 & (x67 ^ (mon_bcd  >>  7)))) };
enum { x71 = ((uint32_t)x70 >> 1) ^ (POLY * (1 & (x70 ^ (day_bcd  >>  0)))) };
enum { x72 = ((uint32_t)x71 >> 1) ^ (POLY * (1 & (x71 ^ (day_bcd  >>  1)))) };
enum { x73 = ((uint32_t)x72 >> 1) ^ (POLY * (1 & (x72 ^ (day_bcd  >>  2)))) };
enum { x74 = ((uint32_t)x73 >> 1) ^ (POLY * (1 & (x73 ^ (day_bcd  >>  3)))) };
enum { x75 = ((uint32_t)x74 >> 1) ^ (POLY * (1 & (x74 ^ (day_bcd  >>  4)))) };
enum { x76 = ((uint32_t)x75 >> 1) ^ (POLY * (1 & (x75 ^ (day_bcd  >>  5)))) };
enum { x77 = ((uint32_t)x76 >> 1) ^ (POLY * (1 & (x76 ^ (day_bcd  >>  6)))) };

enum { x80 = ((uint32_t)x77 >> 1) ^ (POLY * (1 & (x77 ^ (day_bcd  >>  7)))) };
enum { x81 = ((uint32_t)x80 >> 1) ^ (POLY * (1 & (x80 ^ (wday_bcd >>  0)))) };
enum { x82 = ((uint32_t)x81 >> 1) ^ (POLY * (1 & (x81 ^ (wday_bcd >>  1)))) };
enum { x83 = ((uint32_t)x82 >> 1) ^ (POLY * (1 & (x82 ^ (wday_bcd >>  2)))) };
enum { x84 = ((uint32_t)x83 >> 1) ^ (POLY * (1 & (x83 ^ (wday_bcd >>  3)))) };
enum { x85 = ((uint32_t)x84 >> 1) ^ (POLY * (1 & (x84 ^ (wday_bcd >>  4)))) };
enum { x86 = ((uint32_t)x85 >> 1) ^ (POLY * (1 & (x85 ^ (wday_bcd >>  5)))) };
enum { x87 = ((uint32_t)x86 >> 1) ^ (POLY * (1 & (x86 ^ (wday_bcd >>  6)))) };

enum { x90 = ((uint32_t)x87 >> 1) ^ (POLY * (1 & (x87 ^ (wday_bcd >>  7)))) };
enum { x91 = ((uint32_t)x90 >> 1) ^ (POLY * (1 & (x90 ^ (hour_bcd >>  0)))) };
enum { x92 = ((uint32_t)x91 >> 1) ^ (POLY * (1 & (x91 ^ (hour_bcd >>  1)))) };
enum { x93 = ((uint32_t)x92 >> 1) ^ (POLY * (1 & (x92 ^ (hour_bcd >>  2)))) };
enum { x94 = ((uint32_t)x93 >> 1) ^ (POLY * (1 & (x93 ^ (hour_bcd >>  3)))) };
enum { x95 = ((uint32_t)x94 >> 1) ^ (POLY * (1 & (x94 ^ (hour_bcd >>  4)))) };
enum { x96 = ((uint32_t)x95 >> 1) ^ (POLY * (1 & (x95 ^ (hour_bcd >>  5)))) };
enum { x97 = ((uint32_t)x96 >> 1) ^ (POLY * (1 & (x96 ^ (hour_bcd >>  6)))) };

enum { xA0 = ((uint32_t)x97 >> 1) ^ (POLY * (1 & (x97 ^ (hour_bcd >>  7)))) };
enum { xA1 = ((uint32_t)xA0 >> 1) ^ (POLY * (1 & (xA0 ^ (min_bcd  >>  0)))) };
enum { xA2 = ((uint32_t)xA1 >> 1) ^ (POLY * (1 & (xA1 ^ (min_bcd  >>  1)))) };
enum { xA3 = ((uint32_t)xA2 >> 1) ^ (POLY * (1 & (xA2 ^ (min_bcd  >>  2)))) };
enum { xA4 = ((uint32_t)xA3 >> 1) ^ (POLY * (1 & (xA3 ^ (min_bcd  >>  3)))) };
enum { xA5 = ((uint32_t)xA4 >> 1) ^ (POLY * (1 & (xA4 ^ (min_bcd  >>  4)))) };
enum { xA6 = ((uint32_t)xA5 >> 1) ^ (POLY * (1 & (xA5 ^ (min_bcd  >>  5)))) };
enum { xA7 = ((uint32_t)xA6 >> 1) ^ (POLY * (1 & (xA6 ^ (min_bcd  >>  6)))) };

enum { xB0 = ((uint32_t)xA7 >> 1) ^ (POLY * (1 & (xA7 ^ (min_bcd  >>  7)))) };
enum { xB1 = ((uint32_t)xB0 >> 1) ^ (POLY * (1 & (xB0 ^ (sec_bcd  >>  0)))) };
enum { xB2 = ((uint32_t)xB1 >> 1) ^ (POLY * (1 & (xB1 ^ (sec_bcd  >>  1)))) };
enum { xB3 = ((uint32_t)xB2 >> 1) ^ (POLY * (1 & (xB2 ^ (sec_bcd  >>  2)))) };
enum { xB4 = ((uint32_t)xB3 >> 1) ^ (POLY * (1 & (xB3 ^ (sec_bcd  >>  3)))) };
enum { xB5 = ((uint32_t)xB4 >> 1) ^ (POLY * (1 & (xB4 ^ (sec_bcd  >>  4)))) };
enum { xB6 = ((uint32_t)xB5 >> 1) ^ (POLY * (1 & (xB5 ^ (sec_bcd  >>  5)))) };
enum { xB7 = ((uint32_t)xB6 >> 1) ^ (POLY * (1 & (xB6 ^ (sec_bcd  >>  6)))) };

enum { xC0 = ((uint32_t)xB7 >> 1) ^ (POLY * (1 & (xB7 ^ (sec_bcd  >>  7)))) };

__root const static uint32_t TAIL_0x2000 @ 0x0800200C = xC0;

/************* end of file *************/


以上。