交织多么好听的名字,第一次听见这个名字是在移动通信的课程中,当时就对这个名字一亮。交织其实就是把原有的码元顺序打乱进行发送,说白了就是矩阵变换。至于为什么要把原有的码元顺序打乱,是因为现实中传送的码元每一段都有固定的校验位,通过该校验位可以在码元错误较少的情况下进行纠错。而实际中受环境影响的码元是突发性错误,那么我们先经过交织之后传送的码元产生的突发性错误,经过解交织之后就会把原有的突发性错误转变成离散性错误,那么就可以有效的利用校验位进行纠错。这也是交织在通信系统中起到的主要作用,即变突发性错误为随机性错误。
从上面我们可以知道交织就是把原有的码元顺序打乱。一般的码组分为信息位和校验位,在交织的过程中,我们一般分为两步:
1、进行校验位的交织操作
2、对整个码组进行交织操作
上面两步的交织的原理并不一样,接下来分别介绍常见的交织操作。
校验位交织的数学公式如下:
d i = u i f o r 0 < = i < = K 1 d_i=u_i\ \ \ \ for\ \ \ 0<=i<=K1 di=ui for 0<=i<=K1
d K 1 + 360 t + s = u K 1 + Q l d p c s + t f o r 0 < = s < 360 , 0 < = t < = Q l d p c d_{K1+360t+s}=u_{K1+Q_{ldpc}s+t}\ \ \ \ \ for \ \ \ \ 0<=s<360,0<=t<=Q_{ldpc} dK1+360t+s=uK1+Qldpcs+t for 0<=s<360,0<=t<=Qldpc
其中 i i i是码组中第几个码元, K 1 K1 K1是信息位的个数, u i u_i ui是交织前的码元, d i d_i di是交织后的输出码元, Q l d p c = 校 验 位 个 数 / 360 Q_{ldpc}=校验位个数/360 Qldpc=校验位个数/360。
依据校验位交织地址生成公式可知,校验位交织本质是行进列出,若校验位数据向量重新构成矩阵形式,交织前矩阵为Qldpc*360,即依次将向量数据每行写入 360 个数,第 361 个数为第 2 行第 1 列数,依次类推。存入 Rom 地址如图所示,
我们矩阵的时候都是一行一行的看,这里虽然本质上是列进行出,但是我们读地址是一行一行读的,所以这个就相当于矩阵的转置操作。即取数据依次以如下地址取数据,重新生成新读出矩阵。
这里隐含一个非常重要的FPGA技巧,FPGA中矩阵的转置如何使用硬件语言来描述。 等进行讲解Verilog代码的时候,可以着重注意矩阵转置对应的Verilog代码。
上面我们已经进行了检验位交织,将校验位交织与原来的信息位组合成的输入 d i d_{i} di 按列顺序依次写入列旋转交织器,然后按行依次读出完成列旋转交织,每列写入的起始位置由 t c tc tc 决定 ,整个列旋转交织器:
这里解释一下与校验位交织的不同点:
1、校验位交织只有码组的校验位参与交织操作,而列旋转交织是整个码组进行交织操作
2、校验位交织的行是固定的360个元素,而列旋转交织则不是固定值
3、列旋转交织在转置的基础上,每一行均有一个tc的偏移,这也使得在书写FPGA代码的时候要比校验位交织难一点
以16QAM为例,上面的列旋交织主要有2025行8列,其中每一列的tc的值如下:
tc | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
16QAM | 0 | 0 | 0 | 1 | 7 | 20 | 20 | 21 |
列旋转交织器的输入为 d i , 0 < = i < = N d_i,0<=i<=N di,0<=i<=N(其中N是整个码组的长度,在16QAM中N的长度是16200),写入列旋转交织器的 c m c_m cm 列, r n r_n rn 行,则行和列需要满足下面的情况(以16QAM为例):
c m = i / 2025 c_m=i/2025 cm=i/2025
r n = ( i − t c ) % 2025 r_n=(i-tc)\%2025 rn=(i−tc)%2025
由上面的的公式,我们可以写出列旋交织器的输入矩阵与输出矩阵之间的关系,这里同样是按照按照行的顺序写入读出。交织前的地址是:
进行列旋交织之后的地址如下:
这里注意,FPGA的语言叫做硬件描述语言,那么要想才能成功描述上面矩阵的变化,就必须找到上面矩阵的规律,上面的公式规律已经标红,大家可以最终结合相应的FPGA代码进行学习。大家阅读下面的FPGA代码的时候,可以发现一共2025行数据,只有前21行数据是乱的,后面的2000多行数据的计算都是有规律的,这也是FPGA代码中有一个很笨重状态机的原因。大家阅读FPGA代码的时候,一定要联系上面的矩阵,可以便于理解。
经过上面的原理介绍,相信大家对交织的操作有了一定的了解,那么接下来我们给出相应的MATLAB代码供大家更深入的了解。这里说明一下,代码不是博主编写,博主只是在原有的代码上进行了一些更改,可以更方便的理解代码,代码的来源是发烧友学院,在文章的最后,会附上参考网址,需要的同学可以关注一下。
sim_options = struct(...
'CRATE_DATA', '3/5', ... %业务编码码率1/2 3/5 2/3 3/4 4/5 5/6
'CONSTELLATION', '16-QAM' ...
);
tx_nFrame =1;
fid1 = fopen('bint_data_in.txt','r');
DataIn = fscanf(fid1,'%d');
switch sim_options.CRATE_DATA
case '1/2',
Q = 25;
CR=4/9;
case '3/5',
Q = 18;
CR=3/5;
case '2/3',
Q = 15;
CR=2/3;
case '3/4',
Q = 12;
CR=11/15;
case '4/5',
Q = 10;
CR=7/9;
case '5/6',
Q = 8;
CR=37/45;
otherwise
error('sim_options UNKNOWN CODING RATE');
end
%------------------------------------------------------------------------------
% PLP-specific Parameters Definition
%------------------------------------------------------------------------------
CONSTEL = sim_options.CONSTELLATION; % modulation constellation
COD_RATE = CR;
S = 360; % The S of the code
CRATE = sim_options.CRATE_DATA; % The code rate
%------------------------------------------------------------------------------
% Procedure
%---------------------------------------
data = DataIn';
nbint_len = 16200 ; % block size, need to pass this in
int_type = 2;
parity_only = false;
Nc = 8;
switch CONSTEL
case 'QPSK'
int_type = 0;
case '16-QAM'
Tc = [0 0 0 1 7 20 20 21];
end
Nr = nbint_len / Nc;
numIntlvBlocks = floor(length(data)/nbint_len); % Number of interleaving blocks
data = data(1:numIntlvBlocks*nbint_len); %clip the length of the input data
bitIntlvOut = zeros(1,length(data),'single'); %mem preallocation
if int_type == 0
%do no interleaving
bitIntlvOut = data;
elseif int_type == 2
%the type B interleaver
plen = round(nbint_len * (1 - COD_RATE));
%make the parity interleaving table
parity_table = zeros(1,plen);
count = 1;
for scount = 0:S-1
for t = 0:Q-1
parity_table(count) = S * t + scount + 1;
count = count + 1;
end
end
%compare data
fid1 = fopen('mb_parity_addr.txt','w');
fprintf(fid1,'%d\n',parity_table);
%make the Sony interleaving table
if ~parity_only;
code_table = zeros(Nc, Nr);
for cols = 1:Nc
tc = Tc(cols);
rw = (0:Nr-1) - tc;
rwm = mod(rw, Nr) + 1;
rwm = rwm + (cols - 1) * Nr;
code_table(cols, :) = rwm;
end
code_table = reshape(code_table, Nr, []);% + 1;
code_table = reshape(code_table, nbint_len, []);% + 1;
end
intered_parity = zeros(1, plen);
for it=1:numIntlvBlocks
%pick off the parity bits
parity = data( (it * nbint_len) - plen + 1:it * nbint_len);
%interleave the parity bits
intered_parity(parity_table) = parity;
%tack on the parity bits and interleave the whole lot
bitIntlvWr = [data((it-1)*nbint_len+1:it* nbint_len - plen) intered_parity];
%interleave the code word
if ~parity_only; bitIntlvWr = bitIntlvWr(code_table); end
% LDPC block append
bitIntlvOut((it-1)*nbint_len+1:it*nbint_len) = bitIntlvWr;
end
end %if int_type == 1
DataOut.data = bitIntlvOut;
save bitIntlvOut.mat bitIntlvOut
接下来对上面的代码进行简单的描述。
上面是是码组的编码率,下面是码元的调制方式,这里定义了一个结构体,进行一些全局定义。
1、引入输入码元
2、根据全局变量的定义选择不同的参数列表
1、输入码组的个数
2、根据全局变量的设置选择相应tc的值
3、其实这个程序不光可以对一个码组进行交织操作,同时可以对多个码组进行相应的交织操作。看起来程序处理的那么复杂,其实有绝大多数程序是对这方面进行相应的处理,本来博主想更改成一个码组进行处理,这样方便理解一点,但是一想,这是对源代码的不尊重,因为源代码明明功能多,自己非得降低他的功能。
1、计算校验位的个数
2、对校验位进行交织
1、计算列旋交织的输出地址
根据校验位交织、列旋交织的地址进行数据码元的变化,并取出输出码元。
上面只是简要的介绍了上面程序的大体功能,具体的需要大家联合数学原理与MATLAB代码一一相互验证,便可以真正学会该交织操作。
上面讲解完了交织的MATLAB实现,接下来讲解交织的FPGA实现。首先结合前面的理论知识,我先给大家画出程序框图,供大家理解整个设计。
下面整个代码是按照上面的流程进行书写的,当然上面是自己的理解,代码也不是自己书写,也只是改了一些地方。
这里直接给出相应的代码,
顶层模块:
`timescale 1ns/1ps
//////////////////////////////////////////////////////////////////////////////////
//Company: MYMINIEYE
//Engineer: rp lv
//
//Create Date: 2016/03/14 09:41:00
//Design name:
//Module name: tx_Bit_interleaver
//Project name: tx_dvb_t2
//Target Devices: zc706
//Tool Versions: vivado 2015.1
//Description:
//
//Dependencies:
//
//Revision: v_01
//Revision 0.01 -File Created
//Additional Comments
//
//////////////////////////////////////////////////////////////////////////////////
`define UD #1
module tx_Bit_interleaver
(
input sclk ,
input rst_n ,
input s_config_tvalid ,
input [3:0] s_config_tdata ,
input s_data_tvalid ,
input s_data_tdata ,
output reg s_data_tready ,
input s_data_tlast ,
output reg m_data_tvalid ,
output reg m_data_tdata ,
input m_data_tready ,
output reg m_data_tlast
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
reg mode_type ;
reg [2:0] code_rate ;
reg [5:0] Qldpc ;
reg [13:0] Nbch ;
reg [13:0] Pldpc ;
reg bitInter_enb ;
reg [13:0] s_data_cnt ;
reg start_parity ;
reg [13:0] bitInter_ram_addra ;
reg bitInter_ram_wren ;
reg bitInter_ram_dina ;
reg bitInter_ram_wren_reg ;
reg store_over ;
reg start_colrot_reg ;
reg start_colrot_reg2 ;
wire start_colrot ;
reg [13:0] bitInter_ram_addrb ;
wire bitInter_ram_doutb ;
reg colrot_valid_reg ;
reg colrot_valid_reg2 ;
wire [13:0] parity_addr ;
wire parity_valid ;
wire [13:0] colrot_addr ;
wire colrot_valid ;
reg s_data_tvalid_reg ;
wire start_data_invld ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
//====================================================================================
// configure parameter
//====================================================================================
always@(posedge sclk)
begin
if(~rst_n)
begin
mode_type <=`UD 0;
code_rate <= `UD 0;
end
else if (s_config_tvalid)
begin
mode_type <= `UD s_config_tdata[0];
code_rate <= `UD s_config_tdata[3:1];
end
end
always@(posedge sclk)
begin
if(~rst_n)
Qldpc <= `UD 0;
else
begin
case (code_rate)
3'b000 : Qldpc <= `UD 6'd36; // Code Rate = 1/4;
3'b001 : Qldpc <= `UD 6'd25; // Code Rate = 1/2;
3'b010 : Qldpc <= `UD 6'd18; // Code Rate = 3/5;
3'b011 : Qldpc <= `UD 6'd15; // Code Rate = 2/3;
3'b100 : Qldpc <= `UD 6'd12; // Code Rate = 3/4;
3'b101 : Qldpc <= `UD 6'd10; // Code Rate = 4/5;
3'b110 : Qldpc <= `UD 6'd8; // Code Rate = 5/6;
default: Qldpc <= `UD 6'd25; // Code Rate = 1/2;
endcase
end
end
//====================================================================================
// Processing Parity_Interleaver
//====================================================================================
always@(posedge sclk)
begin
if(~rst_n)
Nbch <= `UD 0;
else
begin
case(code_rate)
3'd0 : Nbch <= `UD 14'd3240;
3'd1 : Nbch <= `UD 14'd7200;
3'd2 : Nbch <= `UD 14'd9720;
3'd3 : Nbch <= `UD 14'd10800;
3'd4 : Nbch <= `UD 14'd11880;
3'd5 : Nbch <= `UD 14'd12600;
3'd6 : Nbch <= `UD 14'd13320;
default:Nbch <= `UD 14'd7200;
endcase
end
end
always@(posedge sclk)
begin
if(~rst_n)
Pldpc <= `UD 0;
else
begin
case(code_rate)
3'd0 : Pldpc <= `UD 14'd12960;
3'd1 : Pldpc <= `UD 14'd9000;
3'd2 : Pldpc <= `UD 14'd6480;
3'd3 : Pldpc <= `UD 14'd5400;
3'd4 : Pldpc <= `UD 14'd4320;
3'd5 : Pldpc <= `UD 14'd3600;
3'd6 : Pldpc <= `UD 14'd2880;
default:Pldpc <= `UD 14'd9000;
endcase
end
end
always@(posedge sclk)
begin
bitInter_enb <=`UD s_data_tvalid;
end
always@(posedge sclk)
begin
if(bitInter_enb)
s_data_cnt <= `UD s_data_cnt + 1'b1;
else
s_data_cnt <= `UD 0;
end
always@(posedge sclk)
begin
if(s_data_cnt == Nbch-5'd4) // consider latency
start_parity <= `UD 1'b1;
else
start_parity <= `UD 1'b0;
end
always@(posedge sclk)
begin
if(~rst_n)
bitInter_ram_addra <= `UD 0;
else if(bitInter_enb)
begin
if(parity_valid && mode_type == 1)
bitInter_ram_addra <= `UD parity_addr + Nbch;
else if(mode_type == 1)
bitInter_ram_addra <= `UD bitInter_ram_addra + 1'b1;
else if(mode_type == 0)
bitInter_ram_addra <= `UD bitInter_ram_addra + 1'b1;
end
else
begin
bitInter_ram_addra <= `UD 0;
end
end
always@(posedge sclk)
begin
bitInter_ram_wren <=`UD s_data_tvalid;
bitInter_ram_dina <=`UD s_data_tdata;
end
always@(posedge sclk)
begin
bitInter_ram_wren_reg <=`UD bitInter_ram_wren;
end
always@(posedge sclk)
begin
if(~rst_n || start_colrot)
store_over <= `UD 1'b0;
else if(~bitInter_ram_wren && bitInter_ram_wren_reg)
store_over <= `UD 1'b1;
end
always@(posedge sclk)
begin
if(~rst_n)
start_colrot_reg <= `UD 0;
else if(m_data_tready && store_over)
start_colrot_reg <= `UD 1;
else
start_colrot_reg <= `UD 0;
end
always@(posedge sclk)
begin
start_colrot_reg2 <=`UD start_colrot_reg;
end
assign start_colrot = ~start_colrot_reg2 && start_colrot_reg;
always@(posedge sclk)
begin
if(~rst_n)
bitInter_ram_addrb <=`UD 0;
else if(mode_type == 0 && colrot_valid_reg && bitInter_ram_addrb < 14'd16199) // QPSK
bitInter_ram_addrb <=`UD bitInter_ram_addrb + 1'b1;
else if(mode_type == 1) //16 QAM
bitInter_ram_addrb <=`UD colrot_addr;
else
bitInter_ram_addrb <=`UD 0;
end
always@(posedge sclk)
begin
colrot_valid_reg <= `UD colrot_valid;
colrot_valid_reg2 <= `UD colrot_valid_reg;
m_data_tvalid <= `UD colrot_valid_reg2;
end
always@(posedge sclk)
begin
m_data_tdata <= `UD bitInter_ram_doutb;
end
always@(posedge sclk)
begin
if(~colrot_valid_reg && colrot_valid_reg2)
m_data_tlast <= `UD 1'b1;
else
m_data_tlast <= `UD 1'b0;
end
always@(posedge sclk)
begin
s_data_tvalid_reg <= `UD s_data_tvalid;
end
assign start_data_invld = ~s_data_tvalid_reg && s_data_tvalid;
always@(posedge sclk)
begin
if(~rst_n)
s_data_tready <= `UD 0;
else if(s_config_tvalid)
s_data_tready <= `UD 1;
else if(start_data_invld)
s_data_tready <= `UD 0;
else if(m_data_tlast)
s_data_tready <= `UD 1;
end
parity_bom_addr_gen parity_bom_addr_gen
(
.sclk(sclk), //input
.rst_n(rst_n), //input
.Qldpc(Qldpc), //input [5:0]
.Pldpc(Pldpc), //input [13:0]
.start_parity(start_parity), //input
.parity_addr(parity_addr), //output reg [13:0]
.parity_valid(parity_valid) //output reg
);
ColRot_bom_addr_gen ColRot_bom_addr_gen
(
.sclk(sclk), // input
.rst_n(rst_n), //input
.start_colrot(start_colrot), // input
.colrot_addr(colrot_addr), // output reg [13:0]
.colrot_valid(colrot_valid) // output reg
);
ldpc_ram ldpc_ram
(
.clka(sclk), // input clka
.wea(bitInter_ram_wren), // input [0 : 0] wea
.addra(bitInter_ram_addra), // input [13 : 0] addra
.dina(bitInter_ram_dina), // input [0 : 0] dina
.clkb(sclk), // input clkb
.addrb(bitInter_ram_addrb), // input [13 : 0] addrb
.doutb(bitInter_ram_doutb) // output [0 : 0] doutb
);
endmodule
校验位交织模块:
`timescale 1ns/1ps
//////////////////////////////////////////////////////////////////////////////////
//Company: MYMINIEYE
//Engineer: rp lv
//
//Create Date: 2016/03/14 09:41:00
//Design name:
//Module name: parity_bom_addr_gen
//Project name: tx_dvb_t2
//Target Devices: zc706
//Tool Versions: vivado 2015.1
//Description:
//
//Dependencies:
//
//Revision: v_01
//Revision 0.01 -File Created
//Additional Comments
//
//////////////////////////////////////////////////////////////////////////////////
`define UD #1
module parity_bom_addr_gen
(
input sclk ,
input rst_n ,
input [5:0] Qldpc ,
input [13:0] Pldpc ,
input start_parity ,
output reg [13:0] parity_addr ,
output reg parity_valid
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
reg parity_enb ;
reg [13:0] parity_cnt ;
reg [4:0] s_cnt ;
reg [13:0] S ;
reg [13:0] T ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always@(posedge sclk)
begin
if(~rst_n)
parity_enb <= `UD 1'b0;
else if(start_parity)
parity_enb <= `UD 1'b1;
else if(parity_cnt == Pldpc - 1)
parity_enb <= `UD 1'b0;
end
always@(posedge sclk)
begin
if(~rst_n)
parity_cnt <= `UD 0;
else if(parity_enb)
parity_cnt <= `UD parity_cnt + 1'b1;
else
parity_cnt <= `UD 0;
end
always@(posedge sclk)
begin
if(~rst_n)
begin
s_cnt <= `UD 0;
S <= `UD 0;
end
else if(parity_enb)
begin
if(s_cnt < Qldpc - 1)
begin
s_cnt <= `UD s_cnt + 1'b1;
S <= `UD S + 14'd360;
end
else
begin
s_cnt <= `UD 0;
S <= `UD 0;
end
end
else
begin
s_cnt <= `UD 0;
S <= `UD 0;
end
end
always@(posedge sclk)
begin
if(~rst_n)
T <= `UD 0;
else if(parity_enb)
begin
if(s_cnt == Qldpc -1)
T <= `UD T + 1;
end
else
T <= `UD 0;
end
always@(posedge sclk)
begin
if(parity_enb)
parity_addr <= `UD S + T;
else
parity_addr <= `UD 0;
end
always@(posedge sclk)
begin
parity_valid <= `UD parity_enb;
end
endmodule
列旋交织模块:
`timescale 1ns/1ps
//////////////////////////////////////////////////////////////////////////////////
//Company: MYMINIEYE
//Engineer: rp lv
//
//Create Date: 2016/03/14 13:54:00
//Design name:
//Module name: ColRot_bom_addr_gen
//Project name: tx_dvb_t2
//Target Devices: zc706
//Tool Versions: vivado 2015.1
//Description:
//**************************************
// bom_addr_gen
// 0 2050 4050 8099 10118 12130 14155 16179
// 1 2026 4051 6075 10119 12131 14156 16180
// 2 2027 4052 6076 10120 12132 14157 16181
// ......
//**************************************
//Dependencies:
//
//Revision: v_01
//Revision 0.01 -File Created
//Additional Comments
//
//////////////////////////////////////////////////////////////////////////////////
`define UD #1
module ColRot_bom_addr_gen
(
input sclk ,
input rst_n ,
input start_colrot ,
output reg [13:0] colrot_addr ,
output reg colrot_valid
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter Nc = 3'd7;
parameter Lcr = 14'd16033;//lcr = ldpc - Nc*21=16200 - 8*21 = 16032;
parameter Nr = 11'd2025;
parameter Idle = 24'b0000_0000_0000_0000_0000_0000;
parameter Row_1 = 24'b0000_0000_0000_0000_0000_0001;
parameter Row_2 = 24'b0000_0000_0000_0000_0000_0010;
parameter Row_3 = 24'b0000_0000_0000_0000_0000_0100;
parameter Row_4 = 24'b0000_0000_0000_0000_0000_1000;
parameter Row_5 = 24'b0000_0000_0000_0000_0001_0000;
parameter Row_6 = 24'b0000_0000_0000_0000_0010_0000;
parameter Row_7 = 24'b0000_0000_0000_0000_0100_0000;
parameter Row_8 = 24'b0000_0000_0000_0000_1000_0000;
parameter Row_9 = 24'b0000_0000_0000_0001_0000_0000;
parameter Row_10 = 24'b0000_0000_0000_0100_0000_0000;
parameter Row_11 = 24'b0000_0000_0000_1000_0000_0000;
parameter Row_12 = 24'b0000_0000_0001_0000_0000_0000;
parameter Row_13 = 24'b0000_0000_0010_0000_0000_0000;
parameter Row_14 = 24'b0000_0000_0100_0000_0000_0000;
parameter Row_15 = 24'b0000_0000_1000_0000_0000_0000;
parameter Row_16 = 24'b0000_0001_0000_0000_0000_0000;
parameter Row_17 = 24'b0000_0010_0000_0000_0000_0000;
parameter Row_18 = 24'b0000_0100_0000_0000_0000_0000;
parameter Row_19 = 24'b0000_1000_0000_0000_0000_0000;
parameter Row_20 = 24'b0001_0000_0000_0000_0000_0000;
parameter Row_21 = 24'b0010_0000_0000_0000_0000_0000;
parameter Row_last = 24'b0100_0000_0000_0000_0000_0000;
parameter Gen_end = 24'b1000_0000_0000_0000_0000_0000;
parameter Idle1 = 2'b00;
parameter gen1 = 2'b01;
parameter gen2 = 2'b10;
reg [23:0] state=0;
reg [23:0] state_n=0;
reg [2:0] Col_cal;
reg [13:0] Row_cal;
reg start_colrot_reg;
reg start_colrot_cal;
reg colrot_enb;
reg [13:0]colrot_cnt;
reg [2:0] col_cnt;
reg [13:0]col_addr;
reg [13:0]row_addr;
reg [13:0]gen_taddr;
//====================================================================================
// Generate addr for ColRot bit and valid signal
//====================================================================================
always@(posedge sclk)
begin
if(~rst_n)
state <= `UD 0;
else
state <= `UD state_n;
end
always@(posedge sclk)
begin
start_colrot_reg <= `UD start_colrot;
start_colrot_cal <= `UD start_colrot_reg;
end
always@(*)
begin
state_n = state;
case(state)
Idle : if(start_colrot_cal) state_n = Row_1; else state_n = Idle;
Row_1: if(Col_cal < Nc) state_n = Row_1; else state_n = Row_2;
Row_2: if(Col_cal < Nc) state_n = Row_2; else state_n = Row_3;
Row_3: if(Col_cal < Nc) state_n = Row_3; else state_n = Row_4;
Row_4: if(Col_cal < Nc) state_n = Row_4; else state_n = Row_5;
Row_5: if(Col_cal < Nc) state_n = Row_5; else state_n = Row_6;
Row_6: if(Col_cal < Nc) state_n = Row_6; else state_n = Row_7;
Row_7: if(Col_cal < Nc) state_n = Row_7; else state_n = Row_8;
Row_8: if(Col_cal < Nc) state_n = Row_8; else state_n = Row_9;
Row_9: if(Col_cal < Nc) state_n = Row_9; else state_n = Row_10;
Row_10: if(Col_cal < Nc) state_n = Row_10; else state_n = Row_11;
Row_11: if(Col_cal < Nc) state_n = Row_11; else state_n = Row_12;
Row_12: if(Col_cal < Nc) state_n = Row_12; else state_n = Row_13;
Row_13: if(Col_cal < Nc) state_n = Row_13; else state_n = Row_14;
Row_14: if(Col_cal < Nc) state_n = Row_14; else state_n = Row_15;
Row_15: if(Col_cal < Nc) state_n = Row_15; else state_n = Row_16;
Row_16: if(Col_cal < Nc) state_n = Row_16; else state_n = Row_17;
Row_17: if(Col_cal < Nc) state_n = Row_17; else state_n = Row_18;
Row_18: if(Col_cal < Nc) state_n = Row_18; else state_n = Row_19;
Row_19: if(Col_cal < Nc) state_n = Row_19; else state_n = Row_20;
Row_20: if(Col_cal < Nc) state_n = Row_20; else state_n = Row_21;
Row_21: if(Col_cal < Nc) state_n = Row_21; else state_n = Row_last;
Row_last: if(Row_cal < Lcr) state_n = Row_last; else state_n = Gen_end;
Gen_end : state_n = Idle;
default : state_n = Idle;
endcase
end
always@(posedge sclk)
begin
if(~rst_n)
Row_cal <= `UD 0;
else if (state_n == Row_last)
Row_cal <= `UD Row_cal + 1'b1;
else
Row_cal <=`UD 0;
end
always @(posedge sclk)
begin
case(state_n)
Idle : Col_cal <= `UD 0;
Row_1: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_2: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_3: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_4: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_5: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_6: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_7: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_8: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_9: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_10: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_11: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_12: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_13: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_14: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_15: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_16: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_17: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_18: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_19: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_20: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_21: if(Col_cal == Nc) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
Row_last:if(Row_cal == Lcr) Col_cal <=`UD 0; else Col_cal <=`UD Col_cal + 1'b1;
default : Col_cal <=`UD 0;
endcase
end
//====================================================================================
// Generate Rotate addr for Ldpc Block
//====================================================================================
always@(posedge sclk)
begin
case(state)
Idle: colrot_addr <=`UD 0;
Row_1: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_2: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_3: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_4: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_5: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_6: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_7: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_8: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_9: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_10: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_11: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_12: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_13: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_14: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_15: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_16: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_17: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_18: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_19: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_20: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_21: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr + 12'd2025 - 5'd21;
Row_last: if (Col_cal < 3'd3) colrot_addr <=`UD gen_taddr;
else if (Col_cal == 3'd3) colrot_addr <=`UD gen_taddr - 5'd1;
else if (Col_cal == 3'd4) colrot_addr <=`UD gen_taddr - 5'd7;
else if (Col_cal == 3'd5) colrot_addr <=`UD gen_taddr - 5'd20;
else if (Col_cal == 3'd6) colrot_addr <=`UD gen_taddr - 5'd20;
else if (Col_cal == 3'd7) colrot_addr <=`UD gen_taddr - 5'd21;
Gen_end : colrot_addr <=`UD 0;
default : colrot_addr <=`UD 0;
endcase
end
//====================================================================================
// Generate normal addr , so that easy to get rotate addr
//====================================================================================
always@(posedge sclk)
begin
if(~rst_n)
colrot_enb <= `UD 1'b0;
else if(start_colrot)
colrot_enb <= `UD 1'b1;
else if(colrot_cnt == 14'd16199)
colrot_enb <= `UD 1'b0;
end
always@(posedge sclk)
begin
if(~rst_n)
colrot_cnt <= `UD 0;
else if(colrot_enb)
colrot_cnt <= `UD colrot_cnt + 1'b1;
else
colrot_cnt <= `UD 0;
end
always@(posedge sclk)
begin
if(~rst_n)
begin
col_cnt <= `UD 0;
col_addr <= `UD 0;
end
else if(colrot_enb)
begin
if(col_cnt < Nc)
begin
col_cnt <= `UD col_cnt + 1'b1;
col_addr <= `UD col_addr + 14'd2025;
end
else
begin
col_cnt <= `UD 0;
col_addr <= `UD 0;
end
end
else
begin
col_cnt <= `UD 0;
col_addr <= `UD 0;
end
end
always@(posedge sclk)
begin
if(~rst_n)
row_addr <= `UD 0;
else if(colrot_enb )
begin
if(col_cnt == Nc)
row_addr <= `UD row_addr + 1;
end
else
begin
row_addr <= `UD 0;
end
end
always@(posedge sclk)
begin
if(colrot_enb)
gen_taddr <= `UD col_addr + row_addr;
else
gen_taddr <= `UD 0;
end
//====================================================================================
// Generate Rotate addr valid signal
//====================================================================================
always @(posedge sclk)
begin
if(~rst_n)
colrot_valid <= `UD 0;
else if(state_n == Idle || state_n ==Gen_end)
colrot_valid <=`UD 0;
else
colrot_valid <=`UD 1;
end
endmodule
上面着重说一下列旋交织模块中的状态机,我认为状态机完全可以用行计数器来代替,可以很好的减少代码的复杂度。上面的代码,博主学习了一天的时间才看懂上面的逻辑流程,但是相信经过前面的框图与原理性的介绍,大家学习可以快点。
测试代码如下:
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////
// Company: MYMINEYE
// Engineer:rplv
//
// Create Date: 11:32:55 03/15/2016
// Design Name: tx_Bit_interleaver
// Module Name: tb_tx_Bit_interleaver.v
// Project Name: tx_dvb_t2
// Target Device: zc706
// Tool versions: vivado.2015.1
// Description:
//
//
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
`define UD #1
module tb_tx_Bit_interleaver;
// Inputs
reg clk;
reg rstn;
reg s_config_tvalid;
wire [3:0] s_config_tdata;
reg s_data_tvalid;
reg s_data_tdata;
reg s_data_tlast;
reg m_data_tready;
// Outputs
wire s_data_tready;
wire m_data_tvalid;
wire m_data_tdata;
wire m_data_tlast;
reg s_data_tvalid1;
reg s_data_tlast1;
// Instantiate the Unit Under Test (UUT)
tx_Bit_interleaver uut (
.clk(clk),
.rstn(rstn),
.s_config_tvalid(s_config_tvalid),
.s_config_tdata(s_config_tdata),
.s_data_tvalid(s_data_tvalid1),
.s_data_tdata(s_data_tdata),
.s_data_tready(s_data_tready),
.s_data_tlast(s_data_tlast1),
.m_data_tvalid(m_data_tvalid),
.m_data_tdata(m_data_tdata),
.m_data_tready(m_data_tready),
.m_data_tlast(m_data_tlast)
);
//=========================================================================
// config parameter
//=========================================================================
reg mode_type = 1'b1;
reg [2:0]code_rate = 3'd2;
assign s_config_tdata [0] = mode_type;
assign s_config_tdata [3:1] = code_rate;
//=========================================================================
// initial
//=========================================================================
initial begin
s_config_tvalid = 0;
s_data_tvalid = 0;
rstn = 0;
clk = 0;
m_data_tready = 1;
repeat(10) @(posedge clk);#1;
rstn = 1;
repeat(5) @(posedge clk);#1;
s_config_tvalid = 1;
repeat(1) @(posedge clk);#1;
s_config_tvalid = 0;
repeat(1) @(posedge clk);#1;
s_data_tvalid = 1;
s_data_tlast = 0;
repeat(16200*1-1) @(posedge clk);#1;//QPSK = 8100,
//repeat(4049) @(posedge clk);#1;//QAM = 4050;
s_data_tvalid = 1;
s_data_tlast = 1;
repeat(1) @(posedge clk);#1;//
s_data_tvalid = 0;
s_data_tlast = 0;
end
always@(posedge clk)
begin
s_data_tvalid1 <= `UD s_data_tvalid;
s_data_tlast1 <= `UD s_data_tlast;
end
//=========================================================================
// input
//=========================================================================
integer fid1;
initial
begin
fid1 = $fopen("bint_data_in.txt","r");
end
always@(posedge clk)
begin
if(s_data_tvalid)
$fscanf(fid1,"%d",s_data_tdata);
end
//=========================================================================
// output
//=========================================================================
integer fid2;
initial
begin
fid2 = $fopen("ms_bitInter_sim.txt","w");
end
always@(posedge clk)
begin
if(m_data_tvalid)
$fwrite(fid2,"%b\n",m_data_tdata);
end
always #5 clk = ~clk;
//=========================================================================
// compare addr
//=========================================================================
wire parity_valid = tb_tx_Bit_interleaver.uut.parity_valid;
wire signed [13:0] parity_addr = tb_tx_Bit_interleaver.uut.parity_addr;
integer fid3;
initial
begin
fid3 = $fopen("ms_parity_addr.txt","w");
end
always@(posedge clk)
begin
if(parity_valid)
$fwrite(fid3,"%d\n",parity_addr+1'b1);
end
endmodule
从上面我们可以看出测试代码将交织后的码元输出到一个txt文件中,然后我们就可以在MATLBA中与MATLAB生成的交织后的码元相互验证,证明FPGA侧交织的正确性。
MATLAB验证代码如下:
clc ;
clear all;
load bitIntlvOut.mat
data_lab = bitIntlvOut;
tx_nFrame =1;
bitInter_data_lab = [data_lab'];
fid1 = fopen('ms_bitInter_sim.txt','r');
bitInter_data_sim = fscanf(fid1,'%d');
start_Idx = length(bitInter_data_lab)*(tx_nFrame - 1);
if(isempty(bitInter_data_sim))
bitInter_data_result = 0;
else
bitInter_data_result = sum(abs(bitInter_data_lab - bitInter_data_sim(start_Idx+1:start_Idx+length(bitInter_data_lab))));
end
a = bitInter_data_result
最终测试的结果FPGA与MATLAB交织后的码元完全相同。如下图:
相信大家在学习算法的FPGA实现的时候都掌握了上面的流程,就是先在MATLAB中实现,然后再在FPGA中实现,交互验证实现的正确性。
[1]、电子发烧友学院
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: