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}};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
LFSR代表线性反馈移位寄存器,它是一种在FPGA内部有用的设计。 LFSR易于合成,这意味着它们占用的资源相对较少,并且可以在FPGA内部以很高的时钟速率运行。 使用LFSR可以使许多应用受益,包括:
- 计数器(Counters)
- 测试码型发生器(Test Pattern Generators)
- 数据加扰(Data Scrambling)
- 密码学(Cryptography)
线性反馈移位寄存器实现为FPGA内部的一系列触发器,这些触发器连接在一起作为移位寄存器。 移位寄存器链的多个抽头用作XOR或XNOR门的输入。 然后,此门的输出用作对移位寄存器链开始的反馈,因此用作LFSR中的反馈。
例如5bit的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
//
// Company:
// Engineer: Reborn Lee
// Create Date: 2020/06/01 12:50:38
// Design Name:
// Module Name: lfsr
// Revision 0.01 - File Created
// Additional Comments:
//
//
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
仿真测试
给出一个简单的仿真测试:
`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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
仿真结果:
代码提示
值得注意的是r_LFSR的定义,内部位从1到NUM_BITS,而非0到NUM_BITS -1;
reg [NUM_BITS:1] r_LFSR = 0;
- 1
这就意味着移位代码这样写:
r_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; //left right
- 1
也就是低位往高位移,也即左移。
这是根据这张图来的:
至于仿真文件中对仿真输入设计的也十分简单,就是单纯让种子为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) );
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在设计文件内部,r_LFSR初始值就是为0,因此,不给种子也可以,反正i_Seed_DV本身在测试文件中就无效。
关于反馈多项式是如何确定的呢?
正是上面提供的文档:
抽头确定
此表列出了最大长度为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也称为均匀奇偶校验电路。
- 请注意,此表中描述的连接不一定唯一;某些其他连接也可能导致最大长度序列。
参考资料
交个朋友
文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。
原文链接:reborn.blog.csdn.net/article/details/106473130
- 点赞
- 收藏
- 关注作者
评论(0)