内置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的位置(12323184560000789),还是填在后面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 *************/
以上。