基于FPGA交织的实现

项目简述

交织多么好听的名字,第一次听见这个名字是在移动通信的课程中,当时就对这个名字一亮。交织其实就是把原有的码元顺序打乱进行发送,说白了就是矩阵变换。至于为什么要把原有的码元顺序打乱,是因为现实中传送的码元每一段都有固定的校验位,通过该校验位可以在码元错误较少的情况下进行纠错。而实际中受环境影响的码元是突发性错误,那么我们先经过交织之后传送的码元产生的突发性错误,经过解交织之后就会把原有的突发性错误转变成离散性错误,那么就可以有效的利用校验位进行纠错。这也是交织在通信系统中起到的主要作用,即变突发性错误为随机性错误。

数学模型

从上面我们可以知道交织就是把原有的码元顺序打乱。一般的码组分为信息位和校验位,在交织的过程中,我们一般分为两步:
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的值如下:

tc01234567
16QAM00017202021

列旋转交织器的输入为 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=(itc)%2025
由上面的的公式,我们可以写出列旋交织器的输入矩阵与输出矩阵之间的关系,这里同样是按照按照行的顺序写入读出。交织前的地址是:
在这里插入图片描述
进行列旋交织之后的地址如下:
在这里插入图片描述
这里注意,FPGA的语言叫做硬件描述语言,那么要想才能成功描述上面矩阵的变化,就必须找到上面矩阵的规律,上面的公式规律已经标红,大家可以最终结合相应的FPGA代码进行学习。大家阅读下面的FPGA代码的时候,可以发现一共2025行数据,只有前21行数据是乱的,后面的2000多行数据的计算都是有规律的,这也是FPGA代码中有一个很笨重状态机的原因。大家阅读FPGA代码的时候,一定要联系上面的矩阵,可以便于理解。

交织的MATLAB实现

经过上面的原理介绍,相信大家对交织的操作有了一定的了解,那么接下来我们给出相应的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代码一一相互验证,便可以真正学会该交织操作。

交织的FPGA实现

上面讲解完了交织的MATLAB实现,接下来讲解交织的FPGA实现。首先结合前面的理论知识,我先给大家画出程序框图,供大家理解整个设计。
在这里插入图片描述
下面整个代码是按照上面的流程进行书写的,当然上面是自己的理解,代码也不是自己书写,也只是改了一些地方。

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

上面着重说一下列旋交织模块中的状态机,我认为状态机完全可以用行计数器来代替,可以很好的减少代码的复杂度。上面的代码,博主学习了一天的时间才看懂上面的逻辑流程,但是相信经过前面的框图与原理性的介绍,大家学习可以快点。

交织的GPGA测试代码

测试代码如下:

`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]、电子发烧友学院

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页