引言
卷積碼是一種信道糾錯編碼,在通信中具有廣泛的應用。在發送端根據生成多項式進行卷積碼編碼,在接收端根據維特比(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設計論壇】歡迎添加關注!文章轉載請注明出處。
-
FPGA
+關注
關注
1629文章
21736瀏覽量
603421 -
算法
+關注
關注
23文章
4612瀏覽量
92901 -
卷積碼
+關注
關注
0文章
19瀏覽量
9314
原文標題:卷積碼編碼及維特比譯碼(Viterbi)算法的原理及其FPGA實現
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論