官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师
您的当前位置:主页-old > 新闻中心 > FPGA技术教程 > 信号处理 >

基于FPGA的汉明码编码解码设计

发布时间:2020-03-09   作者:admin 浏览量:

实验简述:


本实验的目的是实现汉明纠错码的编码和解码


一、汉明码简介


汉明码,是在电信领域的一种线性调试码,以发明者理查德 卫斯理 汉明的名字命名。汉明码在传输的消息流中插入验证码,当计算机存储或移动数据时,可能会产生数据位错误,以侦测并更正单一比特错误。由于汉明编码简单,他们被广泛应用于内存。


与其他的错误校验码类似,汉明码也利用了奇偶校验位的概念,通过在数据位后面增加一些比特,可以验证数据的有效性。利用一个以上的校验位,汉明码不仅可以检验数据是否有效,还能在数据出错的情况下指明错误的位置。(汉明码可以检测两位错误,纠正一位错误)。


二、编码规则


理解汉明码首先要理解奇偶校验,奇校验就是在一串编码里增加一位校验位使这一串编码里的1的个数位奇数。偶校验同理,使编码里1的个数为偶数。


汉明码的编码位数n与纠错码的位数k的关系:2^k >= n+k+1。这里给出常用的n和k的值:

n

1

2-4

5-11

12-26

27-57

58-120

k

2

3

4

5

6

7

我们将纠错码加入到相应的编码里,纠错码的位置必须在2^n位置上。以10101100为例进行编码。这个序列为8位,需要4个纠错码。

我们先将序列从1到8编号

1

2

3

4

5

6

7

8

1

0

1

0

1

1

0

0


然后将纠错码(p1,p2,p3,p4)加到这个序列里 的2^n的位置,并用二进制重新编号

0001

0010

0011

0100

0101

0110

0111

1000

1001

1010

1011

1100

p1

p2

1

p3

0

1

0

p4

1

1

0

0


然后我们要求出p1,p2,p3,p4的值,先将上面的序列分组编号为xxx1的分为一组,xx1x的分为一组,x1xx的分为一组,1xxx的分为一组:

xxx1:p1,1,0,0,1,0

xx1x:p2,1,1,0,1,0

x1xx:p3,0,1,0,0

1xxx:p4,1,1,0,0


我们采用偶校验,所以p1 = 1,p2 = 1, p3 = 1, p4 = 0。这样我们就得到了10101100的汉明码111101001100。


那么汉明码是如何来纠错的呢?


我们将p4,p3,p2,p1按这个顺序排列得到0111,这个就是出错的位数,由于是二进制传输,所以就将相应位取反就可以得到正确的序列了。


三、FPGA实现


对于p1,p2,p3,p4的计算在用fpga实现时只需进行按位异或就行。输入数据位8位,需要四个纠错位。


1、顶层架构



信号说明:


信号

功能

说明

clk

工作时钟

外部输入

rst_n

系统复位

外部输入

data

输入数据

外部输入

wren

写使能

外部输入

rden

读使能

外部输入

q

输出数据

输出

hc_out

经过汉明编码后输出

输出

hc_in

输入的汉明码

外部输入


顶层代码:



2、 编码模块


编码模块只需将分组之后的每一组数据(不包括p)按位异或后赋值给p就可以。


编码模块代码:



3、解码模块


解码模块只需判断哪位出错,然后取反,并将纠错位删除即可。


解码模块代码:



module hamming_decoder(clk, rst_n, rden, q, hc_in);


   input clk, rst_n;

   input rden;

   output reg [7:0] q;

   input [11:0] hc_in;

   

   wire g0_error, g1_error, g2_error,g3_error;

   

   assign g0_error = hc_in[10] ^ hc_in[8] ^ hc_in[6] ^ hc_in[4] ^ hc_in[2] ^ hc_in[0];

   assign g1_error = hc_in[10] ^ hc_in[9] ^ hc_in[6] ^ hc_in[5] ^ hc_in[2] ^ hc_in[1];

   assign g2_error = hc_in[11] ^ hc_in[6] ^ hc_in[5] ^ hc_in[4] ^ hc_in[3];

   assign g3_error = hc_in[11] ^ hc_in[10] ^ hc_in[9] ^ hc_in[8] ^ hc_in[7];

   

   always @ (posedge clk or negedge rst_n)begin

       if(!rst_n)

           q <= 0;

       else if(rden)

           case ({g3_error, g2_error, g1_error, g0_error})

               4'b0000 :   q <= {hc_in[11:8], hc_in[6:4], hc_in[2]};

               4'b0001 :   q <= {hc_in[11:8], hc_in[6:4], hc_in[2]};

               4'b0010 :   q <= {hc_in[11:8], hc_in[6:4], hc_in[2]};

               4'b0011 :   q <= {hc_in[11:8], hc_in[6:4], ~hc_in[2]};

               4'b0100 :   q <= {hc_in[11:8], hc_in[6:4], hc_in[2]};

               4'b0101 :   q <= {hc_in[11:8], hc_in[6:5], ~hc_in[4], hc_in[2]};

               4'b0110 :   q <= {hc_in[11:8], hc_in[6], ~hc_in[5], hc_in[4], hc_in[2]};

               4'b0111 :   q <= {hc_in[11:8], ~hc_in[6], hc_in[5], hc_in[4], hc_in[2]};

               4'b1000 :   q <= {hc_in[11:8], hc_in[6], hc_in[5], hc_in[4], hc_in[2]};

               4'b1001 :   q <= {hc_in[11:9], ~hc_in[8], hc_in[6:4], hc_in[2]};

               4'b1010 :   q <= {hc_in[11:10], ~hc_in[9], hc_in[8], hc_in[6:4], hc_in[2]};

               4'b1011 :   q <= {hc_in[11], ~hc_in[10], hc_in[9], hc_in[8], hc_in[6:4], hc_in[2]};

               4'b1100 :   q <= {~hc_in[11], hc_in[10], hc_in[9], hc_in[8], hc_in[6:4], hc_in[2]};

               default :   q <= 0;

           endcase

       else

           q <= 0;

   end


endmodule


四、仿真验证


我们用$random系统函数产生的随机数来作为编码模块数据,用$random系统函数产生的随机数来将hc_out的哪一位取反来模拟噪声。并判断输入的数据和输出的数据是否相等,以验证纠错功能。用$display和$error系统函数来生成报告。


测试文件代码:

module humming_coder12_8_tb;


   reg clk, rst_n;

   reg [7:0] data;

   reg rden, wren;

   wire [11:0] hc_out;

   reg [11:0] hc_in;

   wire [7:0] q;

   

   reg [7:0] temp1, temp2;

   

   humming_coder12_8 DUT(

       .clk(clk),

       .rst_n(rst_n),

       .data(data),

       .wren(wren),

       .q(q),

       .rden(rden),

       .hc_out(hc_out),

       .hc_in(hc_in)

   );

   

   integer pn, i;

   

   initial begin

       pn = 0;

       hc_in = 0;

       

       forever begin

           @ (posedge clk)

           pn = {$random} %12;

           #1

           for (i=0; i<12; i=i+1) begin

               if (i!= pn)

                   hc_in[i] = hc_out[i];

               else

                   hc_in[i] = ~hc_out[i];

           end

       end

   end

   

   always @ (posedge clk)

   begin

       temp1 <= data;

       temp2 <= temp1;

   end


   always @ (*)

   begin

       if (wren) begin

           #1

           if (temp2 == q)

               $display("OK:time=%0t data=%0d q=%0d", $time, temp2, q);

           else

               $error("ERROR:time=%0t data=%0d q=%0d", $time, temp2, q);

       end

   end

       

   initial begin

       clk = 1;

       rst_n = 0;

       data = 0;

       rden = 0;

       wren = 0;

       

       #200

       @ (posedge clk)

       rst_n = 1;

       

       #200

       forever begin

           @ (posedge clk)

           wren = 1;

           data = {$random} % 9'b10000_0000;

           @ (posedge clk)

           wren = 1;

           data = {$random} % 9'b10000_0000;

           rden = 1;

       end

   end


   always #10 clk = ~clk;


   initial #5000 $stop;


endmodule

生成的报告,我们可以看到错误的数据可以被修改成原来正确的数据,证明我们的编码解码模块功能正确。



   拓展阅读