卷积码编码及译码算法的基本原理

描述

引言

 

      卷积码是一种信道纠错编码,在通信中具有广泛的应用。在发送端根据生成多项式进行卷积码编码,在接收端根据维特比(Viterbi)译码算法进行译码,能够有效抵抗信道噪声的影响,在误码率门限之下可以对传输过程中发生的突发错误进行纠错。

 

1、编码及译码算法的基本原理

 

卷积码编码

       卷积码是一种纠错编码,它将输入的k个信息比特编成n个比特输出,特别适合以串行形式进行传输,时延小。卷积码编码器的一般形式如下图所示。

算法

 

 

     如下图所示为k=1时的编码框图,k=1也是最常用的一种编码器情形:

算法

 

 

译码算法

      卷积码的译码方法有两类:一类是大数逻辑译码,又称门限译码;另一类是概率译码,概率译码又能分为维特比译码和序列译码两种。维特比(Viterbi)译码和序列译码都属于概率译码。当卷积码的约束长度不太大时,与序列译码相比,维特比译码器比较简单,计算速度更快。接下来的译码算法采用的是概率译码中的维特比译码。采用概率译码的一种基本想法是:把已接收序列与所有可能的发送序列做比较,选择其中汉明码距最小的一个序列做为发送序列。

 

编码及译码算法的Matlab实现

       根据如上所述的编译码基本原理,我们可以在Matlab中进行很方便的仿真,Matlab提供了集成化的函数可供调用,进行仿真,如下所示:

算法

 

2、编码算法的FPGA实现

 

      根据卷积码编码的基本原理 ,我们可以根据相应的生成多项式来进行Verilog编码,从而可以很方便的实现卷积码编码的FPGA实现:

 

顶层代码

module  convenc(

//system signals

inputclk, 

inputrst_n,

inputdata_in,

outputreg [1:0] data_out     

);

reg [6:0] conv_reg;

 

always @(posedge clk or negedge rst_n) begin

  if (!rst_n) begin

  conv_reg <= 7'd0;

  end

  else begin

  conv_reg <= {data_in,conv_reg[6:1]};

  end

end  

 

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

data_out <= 2'd0;

end

else begin

    data_out[1] <= conv_reg[6]^conv_reg[5]^conv_reg[4]^conv_reg[3]^conv_reg[0];//o171

data_out[0] <= conv_reg[6]^conv_reg[4]^conv_reg[3]^conv_reg[1]^conv_reg[0];//o133

end

end

endmodule

测试代码

`timescale 1ns/1ps;

module tb();

 

reg clk;

reg rst_n;

reg data_in;

wire [1:0] data_out;

reg bits[255:0];

integer out_file;

integer i;

 

convenc demo(

.clk(clk),

.rst_n(rst_n),

.data_in(data_in),

.data_out(data_out)

);

 

initial

begin

clk = 1'b1;

rst_n = 1'b1;

#5 rst_n = 1'b0;

#5 rst_n =1'b1;

$readmemb("F:/FPGA_DSP/Viterbi/bits.txt",bits);

out_file = $fopen("F:/FPGA_DSP/Viterbi/result.txt","w");//获取文件句柄

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

        data_in = bits[i];

        #10;

        $fwrite(out_file,"%b %b ",data_out[1],data_out[0]);

end

end

always #5 clk = ~clk;

endmodule

仿真结果

算法

 

3、维特比译码(Viterbi)算法的FPGA实现

 

     维特比译码(Viterbi)算法在数学原理上是比较复杂的,从理解算法到实现需要做大量的工作,但是Xilinx的Vivado工具给我们提供了Viterbi decoder IP核,我们可以很方便地调用这个IP核进行算法的FPGA实现和落地。

 

Viterbi decoder IP核输入输出数据格式

       Viterbi decoder IP核的接口是基于AXI-Stream协议的,在之前的文章中已经有提及AXI-Stream协议的握手过程,如果有不懂的可以去看前面的文章,下面主要介绍一下该IP和输入输出数据的基本格式组成:

 

       输入数据:

 

       当IP核作为接收输入数据的时候,扮演的是从机的角色,输入数据的格式如下图所示,下图对应的是编码速率为2的情况。如果编码速率为N,那么数据的位宽相应为N*8。

算法

算法

 

 

 

      当IP核配置为硬判决时,输入数据位宽为1,其余位用0补齐, 当IP核配置为软判决时,输入数据位宽为3-5,其余位用0补齐,DATA_IN1对应高位,DATA_IN0对应低位。

 

      输出数据:

 

      IP核的译码输出数据总是1位,格式如下图所示。

算法

 

 

      

 

     最低位为译码数据,其他数据可以不做深入了解。

 

     另外除了待译码数据的输入端口和译码数据输出端口外,该IP核还可以进行误码率(BER)的计算,其余端口位误码率计算配置端口和结果输出端口,具体详情请参考官方手册pg027。

 

IP核生成流程

     Vivado软件为我们提供了Viterbi译码IP核,可以进行图形化配置然后进行调用和使用,配置参数要与编码过程中的相关参数严格对应,具体过程如下所示:

算法

算法

 

算法

 

算法

 

算法

 

 

 

       在图形化配置IP核完成后,我们提取相应的网表文件在Modelsim环境下进行了仿真,如何在Modelsim环境下仿真Vivado IP核我们在前面也有提及,如有不懂的也可翻阅前面的文章进行学习,相关测试程序如下。

 

`timescale 1 ns / 1 ps

module dec_tb ();

glbl glbl();

reg  aclk;

reg aresetn;

 

reg [15:0]s_axis_data_tdata;

reg s_axis_data_tvalid;

wire s_axis_data_tready;

 

wire [7:0]m_axis_data_tdata;

wire m_axis_data_tvalid;

reg m_axis_data_tready;

 

reg [15:0] s_axis_dstat_tdata;

reg s_axis_dstat_tvalid;

wire s_axis_dstat_tready;

 

wire [15:0]m_axis_dstat_tdata;

wire m_axis_dstat_tvalid;

reg m_axis_dstat_tready;

 

reg codeData[511:0];

reg [9:0]i;

reg [9:0]j;

integer out_file;

initial begin

aclk = 1'b1;

aresetn = 1'b1;

#5 aresetn = 1'b0;

#5 aresetn = 1'b1;

$readmemb("F:/FPGA_DSP/Viterbi/codeData.txt",codeData);

out_file = $fopen("F:/FPGA_DSP/Viterbi/decodeData.txt","w");//获取文件句柄

end

 

always #5 aclk = ~aclk;

 

//送数据

always @(posedge aclk or negedge aresetn) begin

if (!aresetn) begin

s_axis_data_tvalid <= 1'b0;

s_axis_data_tdata <= 16'd0;

i <= 9'd0;

j <= 9'd1;

end

else if (s_axis_data_tready) begin

if(i <= 9'd510)begin

s_axis_data_tvalid <= 1'b1;

s_axis_data_tdata <= {7'd0,codeData[j],7'd0,codeData[i]};

i <= i + 2;

j <= j + 2;

end

else

s_axis_data_tvalid <= 1'b0;

end

end

//取数据

always @(posedge aclk or negedge aresetn) begin

if (!aresetn) begin

m_axis_data_tready <= 1'b1;

end

else if (m_axis_data_tvalid) begin

$fwrite(out_file,"%b ",m_axis_data_tdata[0]);

end

end

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

viterbi_0 your_instance_name (

  .aclk(aclk),                                // input wire aclk

  .aresetn(aresetn),                          // input wire aresetn

  //接收数据时为从设备

  .s_axis_data_tdata(s_axis_data_tdata),      // input wire [15 : 0] s_axis_data_tdata

  .s_axis_data_tvalid(s_axis_data_tvalid),    // input wire s_axis_data_tvalid

  .s_axis_data_tready(s_axis_data_tready),    // output wire s_axis_data_tready

  //发送数据时为主设备

  .m_axis_data_tdata(m_axis_data_tdata),      // output wire [7 : 0] m_axis_data_tdata

  .m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid

  .m_axis_data_tready(m_axis_data_tready),    // input wire m_axis_data_tready

  //BER测量

  .s_axis_dstat_tdata(s_axis_dstat_tdata),    // input wire [15 : 0] s_axis_dstat_tdata

  .s_axis_dstat_tvalid(s_axis_dstat_tvalid),  // input wire s_axis_dstat_tvalid

  .s_axis_dstat_tready(s_axis_dstat_tready),  // output wire s_axis_dstat_tready

  .m_axis_dstat_tdata(m_axis_dstat_tdata),    // output wire [15 : 0] m_axis_dstat_tdata

  .m_axis_dstat_tvalid(m_axis_dstat_tvalid),  // output wire m_axis_dstat_tvalid

  .m_axis_dstat_tready(m_axis_dstat_tready)  // input wire m_axis_dstat_tready

);

endmodule

仿真波形:

算法

 

 

原文标题:卷积码编码及维特比译码(Viterbi)算法的原理及其FPGA实现

文章出处:【微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分