Verilog设计实例(1)线性反馈移位寄存器(LFSR)

写在前面

相关博文
博客首页
注:学习交流使用!

正文

原理

线性反馈移位寄存器(LFSR)的英文全称为:Linear Feedback Shift Register。
赛灵思公司的高速串口IP核示例程序经常以LFSR为例,例如Aurora IP的例子程序:

    //______________________________ Transmit Data  __________________________________   
    //Transmit data when TX_DST_RDY_N is asserted.
    //Random data is generated using XNOR feedback LFSR
    //TX_SRC_RDY_N is asserted on every cycle with data
    always @(posedge USER_CLK)
        if(reset_c)
        begin
            data_lfsr_r          <=  `DLY    16'hABCD;  //random seed value
            TX_SRC_RDY_N    <=  `DLY    1'b1;   
        end
        else if(!TX_DST_RDY_N)
        begin
            data_lfsr_r          <=  `DLY    {!{data_lfsr_r[3]^data_lfsr_r[12]^data_lfsr_r[14]^data_lfsr_r[15]},
                                data_lfsr_r[0:14]};
            TX_SRC_RDY_N    <=  `DLY    1'b0;
        end
   
    //Connect TX_D to the DATA LFSR register
    assign  TX_D    =   {1{data_lfsr_r}};

相关博客链接


LFSR代表线性反馈移位寄存器,它是一种在FPGA内部有用的设计。 LFSR易于合成,这意味着它们占用的资源相对较少,并且可以在FPGA内部以很高的时钟速率运行。 使用LFSR可以使许多应用受益,包括:

  • 计数器(Counters)
  • 测试码型发生器(Test Pattern Generators)
  • 数据加扰(Data Scrambling)
  • 密码学(Cryptography)

线性反馈移位寄存器实现为FPGA内部的一系列触发器,这些触发器连接在一起作为移位寄存器。 移位寄存器链的多个抽头用作XOR或XNOR门的输入。 然后,此门的输出用作对移位寄存器链开始的反馈,因此用作LFSR中的反馈。
例如5bit的LFSR的一种形式:


5位LFSR

运行LFSR时,由各个触发器生成的模式是伪随机的,这意味着它接近随机。 它不是完全随机的,因为从LFSR模式的任何状态,您都可以预测下一个状态。 有一些重要的移位寄存器属性需要注意:

  • LFSR模式是伪随机的。
  • 输出模式是确定性的。 您可以通过了解XOR门的位置以及当前模式来确定下一个状态。
    当抽头使用XOR门时,全0的模式不会出现。 由于0与0异或将始终产生0,因此LFSR将停止运行。
  • 当抽头使用XNOR门时,全1的模式将不会出现。 由于将1与1进行异或运算将始终产生1,因此LFSR将停止运行。
  • 任何LFSR的最大可能迭代次数= 2^Bits-1

更长的LFSR将花费更长的时间来运行所有迭代。 N位LFSR的最大可能迭代次数为2^N-1。
如果您考虑一下,所有N位长的东西的所有可能模式都是2^N。 因此,只有一种模式无法使用LFSR表示。 当使用XOR门时,该模式全为0,而使用XNOR门作为您的反馈门时全为1。
VHDL和Verilog代码创建所需的任何N位宽的LFSR。 它使用多项式(这是LFSR背后的数学方法)为每个位宽创建最大可能的LFSR长度。
因此,对于3位,需要2^3-1 = 7个时钟来运行所有可能的组合;
对于4位:2^4-1 = 15;
对于5位:2^5-1 = 31,依此类推。
我基于XNOR实现 以允许FPGA在LFSR上以全零状态启动。 这是Xilinx发布的所有LFSR模式的完整表。

Verilog实现

下面给出Verilog实现代码:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Module Name: lfsr
//https://blog.csdn.net/Reborn_Lee
//////////////////////////////////////////////////////////////////////////////////


module lfsr #(parameter NUM_BITS = 3)(
   input i_Clk,
   input i_Enable,
 
   // data valid
   input i_Seed_DV,

   // Optional Seed Value
   input [NUM_BITS-1:0] i_Seed_Data,
 
   output [NUM_BITS-1:0] o_LFSR_Data,
   output o_LFSR_Done

    );

  // internal variables
  reg [NUM_BITS:1] r_LFSR = 0;
  reg              r_XNOR;
 
 
  // Purpose: Load up LFSR with Seed if Data Valid (DV) pulse is detected.
  // Othewise just run LFSR when enabled.
    always @(posedge i_Clk)
    begin
      if (i_Enable == 1'b1)
      begin
          if (i_Seed_DV == 1'b1)
            r_LFSR <= i_Seed_Data;
          else
            r_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; //left right
      end
    end

// Create Feedback Polynomials.  Based on Application Note:
  // http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
    always @(*)
    begin
      case (NUM_BITS)
        3: begin
          r_XNOR = r_LFSR[3] ^~ r_LFSR[2];
        end
        4: begin
          r_XNOR = r_LFSR[4] ^~ r_LFSR[3];
        end
        5: begin
          r_XNOR = r_LFSR[5] ^~ r_LFSR[3];
        end
        6: begin
          r_XNOR = r_LFSR[6] ^~ r_LFSR[5];
        end
        7: begin
          r_XNOR = r_LFSR[7] ^~ r_LFSR[6];
        end
        8: begin
          r_XNOR = r_LFSR[8] ^~ r_LFSR[6] ^~ r_LFSR[5] ^~ r_LFSR[4];
        end
        9: begin
          r_XNOR = r_LFSR[9] ^~ r_LFSR[5];
        end
        10: begin
          r_XNOR = r_LFSR[10] ^~ r_LFSR[7];
        end
        11: begin
          r_XNOR = r_LFSR[11] ^~ r_LFSR[9];
        end
        12: begin
          r_XNOR = r_LFSR[12] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];
        end
        13: begin
          r_XNOR = r_LFSR[13] ^~ r_LFSR[4] ^~ r_LFSR[3] ^~ r_LFSR[1];
        end
        14: begin
          r_XNOR = r_LFSR[14] ^~ r_LFSR[5] ^~ r_LFSR[3] ^~ r_LFSR[1];
        end
        15: begin
          r_XNOR = r_LFSR[15] ^~ r_LFSR[14];
        end
        16: begin
          r_XNOR = r_LFSR[16] ^~ r_LFSR[15] ^~ r_LFSR[13] ^~ r_LFSR[4];
          end
        17: begin
          r_XNOR = r_LFSR[17] ^~ r_LFSR[14];
        end
        18: begin
          r_XNOR = r_LFSR[18] ^~ r_LFSR[11];
        end
        19: begin
          r_XNOR = r_LFSR[19] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        20: begin
          r_XNOR = r_LFSR[20] ^~ r_LFSR[17];
        end
        21: begin
          r_XNOR = r_LFSR[21] ^~ r_LFSR[19];
        end
        22: begin
          r_XNOR = r_LFSR[22] ^~ r_LFSR[21];
        end
        23: begin
          r_XNOR = r_LFSR[23] ^~ r_LFSR[18];
        end
        24: begin
          r_XNOR = r_LFSR[24] ^~ r_LFSR[23] ^~ r_LFSR[22] ^~ r_LFSR[17];
        end
        25: begin
          r_XNOR = r_LFSR[25] ^~ r_LFSR[22];
        end
        26: begin
          r_XNOR = r_LFSR[26] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        27: begin
          r_XNOR = r_LFSR[27] ^~ r_LFSR[5] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        28: begin
          r_XNOR = r_LFSR[28] ^~ r_LFSR[25];
        end
        29: begin
          r_XNOR = r_LFSR[29] ^~ r_LFSR[27];
        end
        30: begin
          r_XNOR = r_LFSR[30] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];
        end
        31: begin
          r_XNOR = r_LFSR[31] ^~ r_LFSR[28];
        end
        32: begin
          r_XNOR = r_LFSR[32] ^~ r_LFSR[22] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
 
      endcase // case (NUM_BITS)
    end // always @ (*)

    assign o_LFSR_Data = r_LFSR[NUM_BITS:1];
 
    // Conditional Assignment (?)
    assign o_LFSR_Done = (r_LFSR[NUM_BITS:1] == i_Seed_Data) ? 1'b1 : 1'b0;


endmodule

仿真测试

给出一个简单的仿真测试:

`timescale 1ns / 1ps
module lfsr_tb ();
 
  parameter c_NUM_BITS = 4;
   
  reg r_Clk = 1'b0;
   
  wire [c_NUM_BITS-1:0] w_LFSR_Data;
  wire w_LFSR_Done;
   
  lfsr #(.NUM_BITS(c_NUM_BITS)) LFSR_inst
         (.i_Clk(r_Clk),
          .i_Enable(1'b1),
          .i_Seed_DV(1'b0),
          .i_Seed_Data({c_NUM_BITS{1'b0}}), // Replication
          .o_LFSR_Data(w_LFSR_Data),
          .o_LFSR_Done(w_LFSR_Done)
          );
  
  always @(*)
    #10 r_Clk <= ~r_Clk; 
   
endmodule // LFSR_TB

仿真结果:

5位LFSR仿真示意图

代码提示

值得注意的是r_LFSR的定义,内部位从1到NUM_BITS,而非0到NUM_BITS -1;

  reg [NUM_BITS:1] r_LFSR = 0;

这就意味着移位代码这样写:

 r_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; //left right

也就是低位往高位移,也即左移。
这是根据这张图来的:


5bit LFSR

至于仿真文件中对仿真输入设计的也十分简单,就是单纯让种子为0,也即初始值为0,之后进行反馈移位操作。

lfsr #(.NUM_BITS(c_NUM_BITS)) LFSR_inst
         (.i_Clk(r_Clk),
          .i_Enable(1'b1),
          .i_Seed_DV(1'b0),
          .i_Seed_Data({c_NUM_BITS{1'b0}}), // Replication
          .o_LFSR_Data(w_LFSR_Data),
          .o_LFSR_Done(w_LFSR_Done)
          );

在设计文件内部,r_LFSR初始值就是为0,因此,不给种子也可以,反正i_Seed_DV本身在测试文件中就无效。

关于反馈多项式是如何确定的呢?
正是上面提供的文档:
抽头确定

最大长度LFSR抽头确定示意图

此表列出了最大长度为168位的LFSR计数器的相应抽头。前40位的基本描述和表最初在XCELL中发布,并在1993年和1994年Xilinx数据手册的第9-24页上重印。

  • n位LFSR计数器的最大序列长度可以是2^n-1。在这种情况下,它会经历所有可能的代码排列,除了一个锁定状态。
  • 最大长度的n位LFSR计数器由一个n位移位寄存器组成,该移位寄存器在从最后输出Qn到第一输入D1的反馈路径中具有XNOR。XNOR将锁定状态设为all-one状态,也就是说如果种子为全1,则LFSR将锁定,其最终移位结果永远为1;XOR将锁定状态设为all-zeros状态。
  • 对于普通的Xilinx应用程序,全1的触发器都更容易避免,因为“默认情况下”触发器在全零状态下唤醒。
  • Table3描述了必须用作XNOR输入的输出。LFSR输出通常标记为1到n,1是移位寄存器的第一级,n是最后一级。这与二进制计数器的传统0到(n-1)表示法不同。多输入XNOR也称为均匀奇偶校验电路。
  • 请注意,此表中描述的连接不一定唯一;某些其他连接也可能导致最大长度序列。

参考资料

参考资料1
参考资料2

交个朋友

个人微信公众号: FPGA LAB
FPGA/IC技术交流2020

你可能感兴趣的:(Verilog设计实例(1)线性反馈移位寄存器(LFSR))