基於FPGA交織的實現

項目簡述

交織多麼好聽的名字,第一次聽見這個名字是在移動通信的課程中,當時就對這個名字一亮。交織其實就是把原有的碼元順序打亂進行發送,說白了就是矩陣變換。至於爲什麼要把原有的碼元順序打亂,是因爲現實中傳送的碼元每一段都有固定的校驗位,通過該校驗位可以在碼元錯誤較少的情況下進行糾錯。而實際中受環境影響的碼元是突發性錯誤,那麼我們先經過交織之後傳送的碼元產生的突發性錯誤,經過解交織之後就會把原有的突發性錯誤轉變成離散性錯誤,那麼就可以有效的利用校驗位進行糾錯。這也是交織在通信系統中起到的主要作用,即變突發性錯誤爲隨機性錯誤。

數學模型

從上面我們可以知道交織就是把原有的碼元順序打亂。一般的碼組分爲信息位和校驗位,在交織的過程中,我們一般分爲兩步:
1、進行校驗位的交織操作
2、對整個碼組進行交織操作

上面兩步的交織的原理並不一樣,接下來分別介紹常見的交織操作。

校驗位交織

校驗位交織的數學公式如下:
di=ui    for   0<=i<=K1d_i=u_i\ \ \ \ for\ \ \ 0<=i<=K1
dK1+360t+s=uK1+Qldpcs+t     for    0<=s<360,0<=t<=Qldpcd_{K1+360t+s}=u_{K1+Q_{ldpc}s+t}\ \ \ \ \ for \ \ \ \ 0<=s<360,0<=t<=Q_{ldpc}
其中ii是碼組中第幾個碼元,K1K1是信息位的個數,uiu_i是交織前的碼元,did_i是交織後的輸出碼元,Qldpc=/360Q_{ldpc}=校驗位個數/360

依據校驗位交織地址生成公式可知,校驗位交織本質是行進列出,若校驗位數據向量重新構成矩陣形式,交織前矩陣爲Qldpc*360,即依次將向量數據每行寫入 360 個數,第 361 個數爲第 2 行第 1 列數,依次類推。存入 Rom 地址如圖所示,
在這裏插入圖片描述
我們矩陣的時候都是一行一行的看,這裏雖然本質上是列進行出,但是我們讀地址是一行一行讀的,所以這個就相當於矩陣的轉置操作。即取數據依次以如下地址取數據,重新生成新讀出矩陣。
在這裏插入圖片描述
這裏隱含一個非常重要的FPGA技巧,FPGA中矩陣的轉置如何使用硬件語言來描述。 等進行講解Verilog代碼的時候,可以着重注意矩陣轉置對應的Verilog代碼。

碼組的列旋轉交織

上面我們已經進行了檢驗位交織,將校驗位交織與原來的信息位組合成的輸入did_{i} 按列順序依次寫入列旋轉交織器,然後按行依次讀出完成列旋轉交織,每列寫入的起始位置由tctc 決定 ,整個列旋轉交織器:
在這裏插入圖片描述
這裏解釋一下與校驗位交織的不同點:
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

列旋轉交織器的輸入爲di,0<=i<=Nd_i,0<=i<=N(其中N是整個碼組的長度,在16QAM中N的長度是16200),寫入列旋轉交織器的cmc_m 列,rnr_n 行,則行和列需要滿足下面的情況(以16QAM爲例):
cm=i/2025c_m=i/2025
rn=(itc)%2025r_n=(i-tc)\%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]、電子發燒友學院

總結

創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。或者對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章