前言
我们这一篇来讲讲micrium全家桶的uc-crc。该代码库提供了crc算法进行错误检测edc,使用hamming算法实现ecc错误纠正。ecc算法在nand的tfl中使用。
修改版本,去掉对uc-lib,uc-cpu等的依赖,可以直接单独使用,方便移植。
文件介绍
│ license│ notice│ readme.md│├─cfg│ └─template│ crc_cfg.h│├─ports│ ├─arm│ │ └─iar│ │ ecc_hamming_a.asm│ │ edc_crc_a.asm│ ││ └─arm-cortex-m3│ └─iar│ ecc_hamming_a.asm│ edc_crc_a.asm│└─source crc_util.c crc_util.h ecc.h ecc_hamming.c ecc_hamming.h edc_crc.c edc_crc.h
文件 说明
license/notice/readme.md license使用的apache-2.0
crc_cfg.h 配置文件
ecc_hamming_a.asm 使用汇编实现
hamming_parcalcbitword_32
默认提供了arm和arm-cortex-m3架构iar编译器的版本
crc_cfg.h中#define edc_crc_cfg_optimize_asm_en def_enabled时使用,默认不使
edc_crc_a.asm 使用汇编实现
crc_chksumcalctbl_16bit
crc_chksumcalctbl_16bit_ref
crc_chksumcalctbl_32bit
crc_chksumcalctbl_32bit_ref
默认提供了arm和arm-cortex-m3架构iar编译器的版本
crc_cfg.h中#define edc_crc_cfg_optimize_asm_en def_enabled时使用,默认不使用
crc_util.c/h 实现crcutil_popcnt_32
算法来自于http://en.wikipedia.org/wiki/hamming_weight
ecc_hamming.c/h ecc算法代码
edc_crc.c/h crc算法代码
添加代码到自己的工程
添加uc-crccfgtemplatecrc_cfg.h
uc-crcsource下所有文件到自己的工程目录uc-crc下
并配置头文件包含路径uc-crc
依赖
文件 内容
cpu.h
cpu_core.h
lib_def.h
lib_mem.h cpu_boolean
cpu_int08u
cpu_int16u
cpu_int32u
cpu_addr
cpu_data
cpu_size_t
cpu_word_size_32
def_no
def_yes
def_disabled
def_enabled
def_invalid
def_valid
def_octet_nbr_bits
def_bit
def_bit_00~def_bit_12
def_bit_13
def_bit_15
def_bit_17
def_bit_19
def_bit_21
def_bit_23
def_bit_25
def_bit_27
def_bit_29
def_bit_31
def_bit_set
def_bit_is_set
cpu_sw_exception
mem_val_copy_get_int32u
mem_val_copy_get_intu
mem_val_copy_set_int32u
mem_clr
修改代码
注释掉crc_util.h下的
#include #include
改为
#include
注释掉ecc.h下的
#include #include
注释掉ecc_hamming.h下的
#include #include #include #include
注释掉edc_crc.h下的
#include #include #include
注释掉
ecc_hamming.c下的
mem_clr((void *)p_ecc, hamming_len_octet_ecc); /* init ecc buf for err(s) (see note #6). */
改为
memset((void *)p_ecc, 0, hamming_len_octet_ecc);
前面添加
#include
crc_cfg.h中实现以下依赖
/*********************************************************************************************************** port** note(s) : (1) 以下添加依赖部分移植* ***********************************************************************************************************/ /* ------------------ cpu word-endian order ------------------- */#define cpu_endian_type_none 0u#define cpu_endian_type_big 1u /* big- endian word order (see note #1a). */#define cpu_endian_type_little 2u /* little-endian word order (see note #1b). */#define cpu_cfg_endian_type cpu_endian_type_littletypedef unsigned char cpu_boolean; /* 8-bit boolean or logical */typedef unsigned char cpu_int08u; /* 8-bit unsigned integer */typedef unsigned short cpu_int16u; /* 16-bit unsigned integer */typedef unsigned int cpu_int32u; /* 32-bit unsigned integer */typedef cpu_int32u cpu_addr; /* cpu address type based on address bus size. */typedef cpu_int32u cpu_data; /* cpu data type based on data bus size. */typedef cpu_addr cpu_size_t; /* defines cpu standard 'size_t' size. */#define cpu_word_size_32 4u /* 32-bit word size (in octets). */ /* ----------------- boolean defines ------------------ */#define def_no 0u#define def_yes 1u#define def_disabled 0u#define def_enabled 1u#define def_invalid 0u#define def_valid 1u#define def_octet_nbr_bits 8u#define def_bit(bit) (1ul << (bit)) /* ------------------- bit defines -------------------- */#define def_bit_00 0x01u#define def_bit_01 0x02u#define def_bit_02 0x04u#define def_bit_03 0x08u#define def_bit_04 0x10u#define def_bit_05 0x20u#define def_bit_06 0x40u#define def_bit_07 0x80u#define def_bit_08 0x0100u#define def_bit_09 0x0200u#define def_bit_10 0x0400u#define def_bit_11 0x0800u#define def_bit_12 0x1000u#define def_bit_13 0x2000u#define def_bit_15 0x8000u#define def_bit_17 0x00020000u#define def_bit_19 0x00080000u#define def_bit_21 0x00200000u#define def_bit_23 0x00800000u#define def_bit_25 0x02000000u#define def_bit_27 0x08000000u#define def_bit_29 0x20000000u#define def_bit_31 0x80000000u#define def_bit_set(val, mask) ((val) = ((val) | (mask)))#define def_bit_is_set(val, mask) (((((val) & (mask)) == (mask)) && ((mask) != 0u)) ? (def_yes) : (def_no))#define cpu_sw_exception(err_rtn_val) do { ; } while (1)#define mem_val_copy_get_int32u(addr_dest, addr_src) do { cpu_int08u *destptr = (cpu_int08u *)(addr_dest); cpu_int08u *srcptr = (cpu_int08u *)(addr_src); (*((destptr) + 0)) = (*((srcptr) + 0)); (*((destptr) + 1)) = (*((srcptr) + 1)); (*((destptr) + 2)) = (*((srcptr) + 2)); (*((destptr) + 3)) = (*((srcptr) + 3)); } while (0) #define mem_val_copy_get_intu(addr_dest, addr_src, val_size) do { cpu_size_t _i; for (_i = 0; _i < (val_size); _i++) { (*(((cpu_int08u *)(addr_dest)) + _i)) = (*(((cpu_int08u *)(addr_src)) + _i)); } } while (0)#define mem_val_copy_set_int32u(addr_dest, addr_src) mem_val_copy_get_int32u(addr_dest, addr_src)
测试
ecc
用户代码中#include ”ecc_hamming.h”
调用以下接口
hamming_calc
hamming_chk
hamming_correct
详见test.c
#include #include #include ecc_hamming.htypedef struct{ ecc_err err; char* str;}err_str;err_str s_err_str[]={ {ecc_err_none,no error.}, {ecc_err_correctable,correctable error detected in data.}, {ecc_err_ecc_correctable,correctable error detected in ecc.}, {ecc_err_invalid_arg,argument passed invalid value. }, {ecc_err_invalid_len,len argument passed invalid length.}, {ecc_err_null_ptr,pointer argument passed null pointer.}, {ecc_err_uncorrectable,uncorrectable error detected in data.}}; uint8_t s_buffer[33];uint8_t s_ecc[4];int ecc_main(int argc, char* argv[]){ cpu_int08u ecc[4]; ecc_err_loc err_loc[2]={{0,0},{0,0}}; ecc_err err; for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { s_buffer[i] = i+1; } /* 打印原始数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); cpu_size_t len = (sizeof(s_buffer)/sizeof(s_buffer[0])); cpu_size_t len_buf = (len / 32)*32; cpu_size_t len_buf_ext = len % 32; cpu_int08u* p_buf_ext = (cpu_int08u *)s_buffer + len_buf; hamming_calc(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,&err); if(ecc_err_none != err) { printf(hamming_calc err:%d,err); return -1; } printf(hamming_calc:ecc %#x %#x %#x %#x,s_ecc[0],s_ecc[1],s_ecc[2],s_ecc[3]); /* * 1位数据错误 */ /* data注入错误 */ printf([data err test]); s_buffer[0] ^= 0x80; /* 打印错误数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); if(ecc_fault == hamming_chk(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,err_loc,sizeof(err_loc)/sizeof(err_loc[0]),&err)) { printf(hamming_chk err:%d,err); return -2; } printf(hamming_chk:loc b[%d].b[%d] b[%d].b[%d],err_loc[0].lococtet,err_loc[0].locbit,err_loc[1].lococtet,err_loc[1].locbit); printf(%s,s_err_str[err].str); hamming_correct(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,&err); /* 打印修复后的数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); printf(hamming_correct:ecc %#x %#x %#x %#x,s_ecc[0],s_ecc[1],s_ecc[2],s_ecc[3]); printf(%s,s_err_str[err].str);/* * 1位ecc错误 */ /* data注入错误 */ printf([ecc err test]); s_ecc[1] ^= 0x02; /* 打印错误数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); if(ecc_fault == hamming_chk(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,err_loc,sizeof(err_loc)/sizeof(err_loc[0]),&err)) { printf(hamming_chk err:%d,err); return -2; } printf(hamming_chk:loc b[%d].b[%d] b[%d].b[%d],err_loc[0].lococtet,err_loc[0].locbit,err_loc[1].lococtet,err_loc[1].locbit); printf(%s,s_err_str[err].str); hamming_correct(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,&err); /* 打印修复后的数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); printf(hamming_correct:ecc %#x %#x %#x %#x,s_ecc[0],s_ecc[1],s_ecc[2],s_ecc[3]); printf(%s,s_err_str[err].str);/* * 2位数据错误 */ /* data注入错误 */ printf([2data err test]); s_buffer[2] ^= 0x04; s_buffer[5] ^= 0x10; /* 打印错误数据 */ for(int i=0; i< sizeof(s_buffer)/sizeof(s_buffer[0]); i++) { if(i % 16 == 0) { printf(); } printf(%#x ,s_buffer[i]); } printf(); if(ecc_fault == hamming_chk(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,err_loc,sizeof(err_loc)/sizeof(err_loc[0]),&err)) { printf(hamming_chk err:%d,err); return -2; } printf(hamming_chk:loc b[%d].b[%d] b[%d].b[%d],err_loc[0].lococtet,err_loc[0].locbit,err_loc[1].lococtet,err_loc[1].locbit); printf(%s,s_err_str[err].str); hamming_correct(s_buffer,len_buf,p_buf_ext,len_buf_ext,s_ecc,&err); /* 打印修复后的数据 */ for(int i=0; i= m+n+1。
奇偶校验
奇校验:数据和校验位一起,1的个数为奇数
偶校验:数据和校验位一起,1的个数为偶数
奇偶校验只能发现奇数个位翻转,因为偶数个位翻转奇偶性不变。
汉明码
即奇偶校验的升级,组合。
先根据2^n >= m+n+1计算需要多少个校验位,
然后确认校验位的位置在2^n索引位上(索引从1开始)。
比如7位数据需要4位校验位,2^4 >= 7+4+1,一共11位,从1~11编号索引
写为二进制
0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011
将bit0是1的划分为一组 0001 0011 0101 0111 1001 1011
即1 3 5 7 9 11, 将对应数据位奇校验,放在2^0校验位
将bit1是1的划分为一组 0010 0011 0110 0111 1010 1011
即2 3 6 7 10 11, 将对应数据位奇校验,放在2^1校验位
将bit2是1的划分为一组 0100 0101 0110 0111
即4 5 6 7, 将对应数据位奇校验,放在2^2校验位
将bit3是1的划分为一组 0100 0101 0110 0111
即8 9 10 11, 将对应数据位奇校验,放在2^3校验位
纠错,如果2^0校验位不对bit0写1,如果2^1校验位不对bit1写1,得到的二进制数就是出错位的的索引。
比如以上如果bit5翻转了,索引5是在2^0和2^2组的所以,是101,即索引5的数据有错。
汉明距离
在一个码组集合中,任意两个码字之间对应位上码元取值不同的位的数目定义为这两个码字之间的汉明距离d。例如:(00)与(01)的距离是1,(110)和(101)的距离是2。在一个码组集合中,任意两个编码之间汉明距离的最小值称为这个码组的最小汉明距离。最小汉明距离越大,码组越具有抗干扰能力,即差异越大,冗余越多。即编码只用了一部分,剩余的空着,如果出现了错误则肯定是变成了空着的编码。
比如两位的编码实际可以编码为00 01 10 11,但是我们只用00代表a,10代表b,
那么收到01后,我们知道出错了,那么是哪个出错了呢,00变为01需要变化1位,10变为01需要变化2位,所以所以我们更倾向于是00变化了一位即是a出错了,
所以我们可以认为00和01都代表a
10 11 都代表b
这样实际就是用冗余来提高抗干扰能力,我们粗暴的发两次也是冗余,但是冗余太多了浪费空间,所以可以选择不冗余这么多,这个汉明距离d就是冗余的多少,d越大冗余越大,d越小冗余越小。
l当码组用于检测错误时,设可检测e个位的错误,则d ≥ e + 1
l若码组用于纠错,设可纠错t个位的错误,则 d ≥ 2 ∗ t + 1
即ab之间的距离至少是1才能区分ab, 剩余的空间2t划分一半,靠近a的一半t认为是a,靠近b的一半t认为是b。
l如果码组用于纠正t个错,检测e个错,则d ≥ e + t + 1
nand中的硬件ecc
这里以w25n01gvzeig芯片为例,不同芯片略有差异
一个page大大小是2112字节其中用户区域2048字节+额外区域64字节
将page的用户区域和额外区域都分成4份即sector,
则512字节用户区域对应16字节额外区域。
16字节额外区域如上图
0- 1 :2字节位坏块标记
2- 3 :2字节用户数据ii
4-7 : 4字节用户数据i
8-d: 6字节前面sector数据的ecc校验值
e-f:4-d对应的10字节的ecc校验值
问题:ecc for sector 0本身有误码可以通过ecc for spare检测出来,但是如果ecc for spare本身有误码呢?
芯片自带的ecc校验使能通过寄存器配置(有些型号是默认使能的)
写数据时自动计算ecc并更新到64字节额外区域,读数据时自动校验ecc并进行校正,返回校正完后的正确值。
读数据时可以读状态寄存器确认ecc状态
nand中的软件ecc
256字节2048位,检测1位错误理论上只需要12位校验位即可,
2^12 >= 2048 + 12 + 1。
但是实际一半是纠正1位错误,检测2位错误
所以d ≥ e + t + 1=4
汉明距离至少要是4.
nand实际应用中是按照行列分别校验的
按照一个字节8位x256字节
组成256x8,256行8列的矩阵
256行 16个校验位
8列 6个校验位
总共22位,3个字节,剩余两个bit未用放在高位
如下所示
待办
以上micrium和linux的实现实际都没有实现检测ecc码自身错误的情况,都只能校正数据的1位错误,对于ecc码本身错误一位认为是不可校正错误,这里后续可以考虑优化。
总结
以上介绍了micrium全家桶的uc-crc组件,并修改成无其他依赖,比较好移植使用。其中的ecc在nand中使用,所以重点进行了介绍。不仅仅介绍代码库的使用,同时也分析了原理,只有理论结合实践才能真的用好。crc部分下次可以单独再讲讲。
捷通华声灵云智能外呼助力电信业务推广
MS1681单通道 6 阶高清视频滤波驱动
一种基于CPLD的16位VFC式AD转换器设计
有哪些法拉电容器的应用储能市场?
串并联谐振电路的特性
讲讲Micrium全家桶的uC-CRC算法
高镍三元产能布局和量产进度,风口转向引领行业洗牌
有效输出50W、失真率真0.003%的厚膜合放大器
Intersil推出两款具备业内最快瞬态响应的LDO
使用FPGA量身定制的安全性
美国批准AMD及英特尔向华为供货?关键在于能不能自研
LED异形显示屏定制
GMY002 0-100%PWM转±5V输出模块
土壤腐蚀野外组合测试仪的性能指标
实施工业物联网战略的方案
什么是白炽灯
苹果不行了iphone8跌落冰点?国内手机市场发展迅速,小米7骁龙845将于明年2月首发
Arduino的基础教程
汽车雾灯改装及电路图
UCOS2_STM32F1移植详细过程 (四)