FPGA: RS译码仿真过程
在上一篇中记录了在FPGA中利用RS编码IP核完成信道编码的仿真过程,这篇记录利用译码IP核进行RS解码的仿真过程,带有程序和结果。
1. 开始准备
在进行解码的过程时,同时利用上一篇中的MATLAB仿真程序和编码过程,IP核的下载是同样的地址。解码过程中的参数设置正好对应编码的过程。对0-15的自然数通过RS编码得到的数据进行解码,其中m=4
,n=15
,k=3
,ploy=19
。
2. RS译码IP核
RS译码IP核全名Reed-Solomon Decoder
,具体细节可以参照PDF技术文档,首先看IP核参数设置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R32PpUta-1692190030739)(1.png “rs解码IP核设置1”)]
已经通过RS编码IP核完成了编码的仿真过程,并且通过MATLAB对比对结果进行了验证,所以这个第一个页码的参数直接参照如图设置就可以,与编码是一一对应的,没有什么需要特别的说明。
在下面的Variable Check Symbol Options
不需要勾选。
在第二个参数设置界面,都不需要勾选,勾选的话译码输出的结果会带有校验的数据。
在第三个参数设置界面中,把Reset
选项勾选上。
完成这个IP核的设置。
补充
为了方便利用仿真过程中的译码过程,在之前完成编码过程后添加了一个fifo
方便进行数据处理和信号控制,其中的fifo
IP核的参数设置为如下。
这个fifo
根据需要设置即可,主要是为了编码之后的数据和译码过程使用控制方便。
3. 代码编写
译码过程是在编码基础上添加的,编码过程的参数没有变化,对0-15的数据进行编码,然后再进行译码,在编码和译码过程中间有一个fifo
,其中fifo
的读控制信号利用empty
信号和译码IP的s_ready
信号,fifo
的写信号编码信号的输出valid
信号。详细的逻辑看代码。
代码如下rs_test.v
`timescale 1ns / 1ps
module rs_test(
input clk, //时钟
input rst_n // 复位 高电平复位
// input [7:0] data_in, // 输入的待编码数据
// output [7:0] dataout // 输出的解码数据
);
wire rs_encode_input_tready; // 编码输入准备信号
reg rs_encode_input_tvalid_reg; // 编码输入有效信号
reg rs_encode_input_tready_reg;
wire rs_encode_input_tlast;
reg rs_encode_input_tlast_reg;
wire[7:0] rs_encode_data;
wire rs_encode_output_tvalid;
wire rs_encode_output_tlast;
wire rs_enocde_output_tready;
reg rs_enocde_output_tready_reg;
parameter K = 3; // 对应MATLAB仿真中的k和n的值,这个在IP核设置中已经有体现
parameter N = 4; //
parameter L = 15; // 编码之后的数据长度
reg [3:0] datain_num; // 每一组编码的原始数据个数
reg [5:0] dataout_num; //输出编码数据的个数
wire fifo_full; // fifo 满信号
// 设计输入数据
reg [3:0] datain;
always@(posedge clk)begin
if(~rst_n)begin
datain <= 4'b0;
rs_encode_input_tready_reg <= 1'b0;
rs_encode_input_tvalid_reg <= 1'b0;
rs_encode_input_tlast_reg <= 1'b0;
rs_enocde_output_tready_reg <= 1'b0;
datain_num <= 4'b0;
end
else begin
rs_encode_input_tready_reg <= rs_encode_input_tready;
if(fifo_full==1'b1)begin
rs_encode_input_tvalid_reg <= 1'b0;
end
else begin
rs_encode_input_tvalid_reg <= 1'b1;
end
if(rs_encode_input_tready == 1'b1 && rs_encode_input_tvalid_reg == 1'b1)begin // 在ready 和valid信号都有效的时候才开始编码数据,可以在这里计数编码的个数。
datain <= datain + 4'b1;
datain_num <= 4'b1 + datain_num;
rs_enocde_output_tready_reg <= 1'b1;
end
else begin
end
end
end
// 根据每一组编码的组数来确定数据顺序 控制最后一个tlast信号。
always@(posedge clk)begin
if(~rst_n)begin
rs_encode_input_tlast_reg <= 1'b0; // 这个信号是需要在一组中的最后一个数据时候信号处于高电平 和k的大小对应
end
else begin
if(datain_num >= K)begin
rs_encode_input_tlast_reg <= 1'b1;
end
else begin
rs_encode_input_tlast_reg <= 1'b0; //然后重新置零
end
end
end
wire [3:0] data_in;
assign data_in = datain;
rs_encoder_0 rs_encoder_0_ins ( //latency 5clk
.aclk(clk), // input wire aclk
.aresetn(rst_n), // input wire aresetn
.s_axis_input_tdata(data_in), // input wire [7 : 0] s_axis_input_tdata
.s_axis_input_tvalid(rs_encode_input_tvalid_reg), // input wire s_axis_input_tvalid
.s_axis_input_tready(rs_encode_input_tready), // output wire s_axis_input_tready
.s_axis_input_tlast(rs_encode_input_tlast_reg), // input wire s_axis_input_tlast
.m_axis_output_tdata(rs_encode_data), // output wire [7 : 0] m_axis_output_tdata
.m_axis_output_tvalid(rs_encode_output_tvalid), // output wire m_axis_output_tvalid
.m_axis_output_tready(rs_enocde_output_tready_reg), // input wire m_axis_output_tready
.m_axis_output_tlast(rs_encode_output_tlast) // output wire m_axis_output_tlast
);
// 通过编码模块输出的valid信号和ready信号来记录输出数据的个数
always@(posedge clk)begin
if(~rst_n)begin
dataout_num <= 6'b0;
end
else begin
if(rs_encode_output_tvalid==1'b1 && rs_enocde_output_tready_reg==1'b1)begin
dataout_num <= dataout_num + 6'b1;
if(dataout_num >= 6'd15)begin
dataout_num <= 6'b0;
end
end
else begin
end
end
end
// rs 译码过程
// 在编码之后的数据添加一个fifo 方便管理valid信号和ready信号,减少耦合同时可以比配位宽
wire fifo_empty;
wire fifo_rd_en;
wire[3:0] fifo_data;
reg fifo_flag; // 这个是用来标致第一次从fifo中读取数据的过程
wire [7:0] rs_decode_data_temp;
wire [3:0] rs_decode_data;
//in
wire rs_decode_data_s_ready;
wire rs_decode_data_s_valid;
reg rs_decode_data_s_valid_reg; // 去掉fifo 输出的一个时钟延迟
reg rs_decode_data_s_tlast_reg;
assign fifo_rd_en = rs_decode_data_s_ready && (!fifo_empty);
// out
wire rs_decode_data_m_valid;
wire rs_decode_data_m_tlast;
wire rs_decode_data_m_ready;
// stat
wire [7:0] rs_decode_stat_data;
//wire rs_decode_stat_ready;
wire rs_decode_stat_valid;
always@(posedge clk)begin
if(~rst_n)begin
fifo_flag <= 1'b0;
end
else begin
if(fifo_rd_en==1'b1)begin
fifo_flag <= 1'b1;
end
end
end
always@(posedge clk)begin
if(~rst_n)begin
rs_decode_data_s_valid_reg <= 1'b0;
end
else begin
rs_decode_data_s_valid_reg <= fifo_rd_en;
end
end
fifo_generator_0 fifo_ins( // 这个输出有1clk延迟
.clk(clk), // input wire clk
.srst(~rst_n), // input wire srst
.din(rs_encode_data[3:0]), // input wire [3 : 0] din
.wr_en(rs_encode_output_tvalid), // input wire wr_en
.rd_en(fifo_rd_en), // input wire rd_en
.dout(fifo_data), // output wire [3 : 0] dout
.full(fifo_full), // output wire full
.empty(fifo_empty) // output wire empty
);
// 输入编码中的有效信号
assign rs_decode_data_s_valid = (fifo_flag==1'b1)?fifo_rd_en:rs_decode_data_s_valid_reg; // 在第一次读取的时候 信号跟随reg信号,之后跟随en信号
reg[5:0] decode_num;
always@(posedge clk)begin
if(~rst_n)begin
decode_num = 6'b1;
end
else begin
if(rs_decode_data_s_valid==1'b1)begin
decode_num <= decode_num + 6'b1;
if(decode_num >= 6'd14)begin
decode_num <= 6'b0;
end
end
end
end
// 控制tlast信号
always@(posedge clk)begin
if(~rst_n)begin
rs_decode_data_s_tlast_reg <= 1'b0;
end
else begin //当解码输入进入的数据为一组时,拉高tlast信号;
if(decode_num >= 6'd14)begin
rs_decode_data_s_tlast_reg <= 1'b1;
end
else begin
rs_decode_data_s_tlast_reg <= 1'b0;
end
end
end
rs_decoder_0 rs_decoder_0_ins (
.aclk(clk), // input wire aclk
.aresetn(rst_n), // input wire aresetn
.s_axis_input_tdata(fifo_data), // input wire [7 : 0] s_axis_input_tdata
.s_axis_input_tvalid(rs_decode_data_s_valid), // input wire s_axis_input_tvalid
.s_axis_input_tlast(rs_decode_data_s_tlast_reg), // input wire s_axis_input_tlast
.s_axis_input_tready(rs_decode_data_s_ready), // output wire s_axis_input_tready
.m_axis_output_tdata(rs_decode_data_temp), // output wire [7 : 0] m_axis_output_tdata
.m_axis_output_tvalid(rs_decode_data_m_valid), // output wire m_axis_output_tvalid
.m_axis_output_tready(1'b1), // input wire m_axis_output_tready
.m_axis_output_tlast(rs_decode_data_m_tlast), // output wire m_axis_output_tlast
.m_axis_stat_tdata(rs_decode_stat_data), // output wire [7 : 0] m_axis_stat_tdata
.m_axis_stat_tvalid(rs_decode_stat_valid), // output wire m_axis_stat_tvalid
.m_axis_stat_tready(1'b1) // input wire m_axis_stat_tready
);
assign rs_decode_data = rs_decode_data_temp[3:0];
endmodule
4. 仿真测试
测试程序的testbench
文件和之前保持一致,只需要把实例化的模块名字更改即可。
`timescale 1ns / 1ps
module rs_tb();
reg l_clk;
reg rst_n;
rs_test rs_test_ins(
.clk(l_clk), //时钟
.rst_n(rst_n) // 复位 高电平复位
);
initial l_clk = 1;
always #5 l_clk= !l_clk; //15.625
initial begin
rst_n <= 0;
#40;
rst_n <= 1;
#320;
//#50000000;
#320;
// $stop;
end
endmodule
然后进入仿真过程,对照时序查看结果。
首先看试验大图,其中的蓝色线是解码之后的数据,从数据结果中可以看出每个数据间隔3,正好是编码之前的结果,拉开蓝色线就可以看到具体的数值。因为译码也是存在延时的,所以看起来数据会滞后,蓝色数据线的m_valid
信号对应输出数据有效信号。
这里面有需要注意的地方,首先看仿真结果的前面部分。
这是fifo_rd_en
有效的第一段,由于有1个clk
的时钟延迟,所以把有效信号rs_decode_data_s_valid
信号需要延迟一个clk
,然后看接下来的fifo_rd_en
第二个周期,需要把rs_decode_data_s_valid
信号和fifo_rd_en
信号对齐,否则会丢一个数据,后面的和第二个周期相同,只有第一个需要延迟一个周期,这个在程序中通过fifo_flag
判断是不是第一个周期。
至此完成了译码的过程。