引言
卷积码是一种信道纠错编码,在通信中具有广泛的应用。在发送端根据生成多项式进行卷积码编码,在接收端根据维特比(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设计论坛】欢迎添加关注!文章转载请注明出处。
全部0条评论
快来发表一下你的评论吧 !