【FPGA】线性反馈移位寄存器(LFSR)的Verilog实现
什么是移位寄存器
移位寄存器:是指多个寄存器并排相连,前一个寄存器的输出作为下一个寄存器的输入,寄存器中存放的数据在每个时钟周期向左或向右移动一位。
下面的右移移位寄存器因为左侧没有有效输入,所以在第4个时钟周期,寄存器内就已经没有有效数据了。
反馈移位寄存器:寄存器被移出的数据后又通过某种方式或函数重新连接到了移位寄存器的输入端,从而使得移位寄存器有不断的输出。
线性反馈移位寄存器(Linear-Feedback Shift Register,LFSR):当反馈移位寄存器的反馈函数为线性函数时,就称这个移位寄存器是反馈移位寄存器。LFSR所用的线性反馈函数一般为 异或 或者 同或。
在每个时钟周期,LFSR的新的输入值会被反馈到内部各个寄存器的输入端,输入值中的一部分来源于LFSR的输出端,另一部分来源于LFSR各输出端进行异或运算得到。
什么是LFSR
LFSR的初始值被称为种子(Seed)。由异或门构成的LFSR的种子不能为全0,因为0与0异或永远为0,所以移位寄存器的输出永远都不会变化。同理,由同或门构成的LFSR的种子则不能为全1。
LFSR中的寄存器的个数被称为LFSR的级数。一个3级的LFSR最多同时存放3bit数据。一个n级的LFSR最多只有2^n - 1个状态(因为要排除全0状态 或 全1状态),比如3级的LFSR就只有7个状态。
LFSR的有些位参与反馈,有些位不参与反馈,其中参与的位被称为抽头。因为触发器编号从1开始,因此抽头的取值范围是1~(2^n-1)。
如果设计得当(与抽头有关),那么LFSR产生的状态可以是周期性的。只要选择合适的反馈函数便可使序列的周期达到最大值(2^n-1),周期达到最大值的序列称为m序列( m-sequence )。比如下面的LFSR(假设种子为111)会依次产生
111>110>100>001>010>101>011 这7个状态,然后又重新从111开始循环。
不同的级数如何选取正确的抽头?可以参考下面这个表,表内的抽头选取都是可以保证LFSR能运转到最多状态的。
LFSR的两种分类
目前有两类常用的LFSR:斐波那契LFSR 和 伽罗瓦LFSR,下面分别就进行介绍。
斐波那契LFSR(Fibonacci LFSRs),又被称为多到一型LFSR,即多个触发器的输出通过反馈来驱动一个触发器的输入。下图是一个典型的斐波那契LFSR,反馈抽头为3和2。
伽罗瓦LFSR(Fibonacci LFSRs),又被称为一到多型LFSR,即以个触发器的输出通过反馈来驱动多个触发器的输入。下图是一个典型的伽罗瓦LFSR,反馈抽头同样为3和2。这个伽罗瓦LFSR同样可以产生7级序列,但是序列的排序与斐波那契LFSR产生的序列是不同的。
斐波那契LFSR和伽罗瓦LFSR有同样的功能,但是伽罗瓦LFSR的电路性能要由于斐波那契LFSR,因为它在两个触发器之间只使用了一个异或门(或者同或门)。
Verilog实现与仿真
下面用Verilog分别实现抽头为(8.6.5.4)的8级斐波那契LFSR和伽罗瓦LFSR,8级LFSR的状态数为2^8-1=255个。
.
这个网站LFSR自动工具有一个很好用的LFSR工具,可以自定义抽头、级数,反馈方式和LFSR种类,它可以自动生成Verilog代码和穷举所有的LFSR状态。
斐波那契LFSR
用同或门作为反馈函数生成抽头为(8.6.5.4)的8级斐波那契LFSR,Verilog代码如下:
//8级斐波那契LFSR(多到1型LFSR)设计
//同或门作为反馈函数,反馈多项式为 f(x)=x^8 + x^6 + x^5 + x^4 + 1
module LFSR8_Fib(input clk,input rst, output reg [7:0] lfsr
);always @(posedge clk) beginif(rst)//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便lfsr <= 8'h0; else begin//抽头从1开始为8、6、5、4lfsr[0] <= ~(lfsr[3] ^ lfsr[4] ^ lfsr[5] ^ lfsr[7]);//低位移动到高位lfsr[7:1] <= lfsr[6:0];end
end
生成的LFSR示意图如下:
为了验证生成电路的正确性,需要编写TB文件进行验证。对于这种简单的模块(只有255个状态),最简单的验证办法就是穷举所有状态与正确状态进行对比即可。方法有几种:
方法1:手动对比
移位LFSR软件提供了所有正确的输出,所以我们只需要将正确的向量与仿真出来的波形结果一一对比即可,这种方法简单,但是效率较低,且容易出错。编写的TB文件如下:
`timescale 1ns/1ns
module tb_LFSR8_Fib();
//信号声明
reg clk;
reg rst;
reg [7:0] cnt; //记录状态个数,一共255个(没有全1状态)
wire [7:0] lfsr;//模块实例化
LFSR8_Fib inst_LFSR8_Fib(.clk (clk),.rst (rst), .lfsr (lfsr)
); //生成时钟信号
initial beginclk = 1'b1;forever #5 clk = ~clk;
end//生成复位信号
initial beginrst = 1'b1; //复位#45 rst = 1'b0; //取消复位
end//仿真过程
initial beginwait(cnt == 255); //所有状态都仿真结束#10 $stop; //关闭仿真
end//记录状态个数,每个有效时钟周期加1
always@(posedge clk)beginif(rst)cnt <= 8'd0;else begincnt <= cnt + 1'd1;end
endendmodule
仿真结果如下:
与正确向量一一对比即可,发现仿真结果无误。
方法2:将仿真结果打印到Tcl窗口,然后复制到文件,再与正确向量对比。对比方式可以是手动对比,也可以是用插件自动进行对比。
这种方法只需要部分修改TB文件:
//记录状态个数,每个有效时钟周期加1
always@(posedge clk)beginif(rst)cnt <= 8'd0;else begincnt <= cnt + 1'd1;$display("%h",lfsr); //打印每一个状态到窗口end
end
然后Tcl窗口就打印出了仿真结果:
方法3:基本与方法2类似,只不过将将仿真结果直接打印到文件,再与正确向量对比。对比方式可以是手动对比,也可以是用插件自动进行对比。TB文件如下:
`timescale 1ns/1ns
module tb_LFSR8_Fib();
//信号声明
reg clk;
reg rst;
reg [7:0] cnt; //记录状态个数,一共255个(没有全1状态)
wire [7:0] lfsr;//定义文件句柄
integer handle_file_out; //模块实例化
LFSR8_Fib inst_LFSR8_Fib(.clk (clk),.rst (rst), .lfsr (lfsr)
); //生成时钟信号
initial beginclk = 1'b1;forever #5 clk = ~clk;
end//生成复位信号
initial beginrst = 1'b1; //复位#45 rst = 1'b0; //取消复位
end//仿真过程
initial beginwait(cnt == 255); //所有状态都仿真结束#10 $fclose(handle_file_out); //关闭文件$stop; //关闭仿真
end//打开文件file_out,相对路径需要与TB文件在同一目录下
initial begin//handle_file_out = $fopen("file_out.txt","w");//相对路径handle_file_out = $fopen("G:/file_out.txt","w");//绝对路径
end//记录状态个数,每个有效时钟周期加1
always@(posedge clk)beginif(rst)cnt <= 8'd0;else begincnt <= cnt + 1'd1;//$display("%h",lfsr); //打印每一个状态到窗口//打印每一个状态到文件$fdisplay(handle_file_out,"%h",lfsr); end
endendmodule
打开file_out文件,可以看到数据都已经被保存好了:
方法4:在TB文件中读取正确向量并自动与仿真结果一一对比,若对比有误则输出某个标志信号。TB文件如下:
`timescale 1ns/1ns
module tb_LFSR8_Fib();
//信号声明
reg clk;
reg rst;
wire [7:0] lfsr;reg [7:0] cnt; //记录状态个数,一共255个(没有全1状态)
reg [7:0] lfsr_gold [0:254]; //构建一个数组来存储正确向量,位宽为8,个数为255个
reg flag; //错误标志,1表示对比错误;0表示对比正确
reg [7:0] cnt_error; //错误计数器//模块实例化
LFSR8_Fib inst_LFSR8_Fib(.clk (clk),.rst (rst), .lfsr (lfsr)
); //生成时钟信号
initial beginclk = 1'b1;forever #5 clk = ~clk;
end//生成复位信号
initial beginrst = 1'b1; //复位#45 rst = 1'b0; //取消复位
end//读取正确向量
initial begin$readmemh("G:/file_in.txt",lfsr_gold); //绝对路径
end//仿真过程
initial beginwait(cnt == 255); //所有状态都仿真结束if (cnt_error == 0)//打印仿真成功信息$display("simulation succeed!");else//打印仿真错误信号$display("simulation failed,there is %d errors!",cnt_error);#10 $stop; //关闭仿真
end//记录状态个数,每个有效时钟周期加1
always@(posedge clk)beginif(rst)begincnt <= 8'd0;flag <= 1'b0;cnt_error <= 8'd0;end else begincnt <= cnt + 1'd1;if(lfsr_gold[cnt] != lfsr)begin //如果对比有误//$display("cnt=%d is wrong",cnt); //打印错误的地方flag <= 1'b1; //拉高错误标志cnt_error <= cnt_error + 1; //错误计数器加1endelse beginflag <= 1'b0;cnt_error <= cnt_error;end end
endendmodule
为了对比仿真结果,我故意把正确向量的第2个数据改成错误数据,仿真结果如下:
Tcl窗口也打印了错误:
把正确向量的错误修正后再仿真,仿真无误,打印的信息如下:
这种方式可以自动对比正确向量与仿真结果,大大提高了效率。
伽罗瓦LFSR
用同或门作为反馈函数生成抽头为(8.6.5.4)的8级伽罗瓦LFSR,Verilog代码如下:
//8级伽罗瓦LFSR(1到多型LFSR)设计
//同或门作为反馈函数,反馈多项式为 f(x)=x^8 + x^6 + x^5 + x^4 + 1
module LFSR8_Gal(input clk,input rst, output reg [7:0] lfsr
);wire feedback;;assign feedback = lfsr[7];always @(posedge clk)beginif(rst)//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便lfsr <= 8'h0;else begin//抽头从1开始为8、6、5、4lfsr[0] <= feedback;lfsr[1] <= lfsr[0];lfsr[2] <= lfsr[1];lfsr[3] <= lfsr[2];lfsr[4] <= lfsr[3] ~^ feedback;lfsr[5] <= lfsr[4] ~^ feedback;lfsr[6] <= lfsr[5] ~^ feedback; lfsr[7] <= lfsr[6];end
endendmodule
电路示意图如下:
仿真脚本依然用上面的即可,这里就不啰嗦了。
禁止状态的处理
用同或门作为反馈函数的LFSR是禁止使用全1状态的,因为全1的同或还是1,会导致移位寄存器一直处于全1状态出不来。这是的n级的LFSR只有2^n - 1 个状态,比一般的计数器状态少1个。如果需要完善这种禁止状态的处理,可以增加一部分电路来改变。
斐波那契LFSR可以增加一个判断寄存器是否为全1的电路,并将其输出连接到同或门,示意图如下:
如果不为全1状态,则全1判断电路输出为0,0异或任何数都等于该数本身,即不会对原有电路造成影响。如果为全1状态,则全1判断电路输出为1,此时同或门的输出为0,所以下一个状态的最低位即为0,也就是说跳出了全1状态。
这部分的Verilog代码如下:
//8级斐波那契LFSR(多到1型LFSR)设计
//同或门作为反馈函数,反馈多项式为 f(x)=x^8 + x^6 + x^5 + x^4 + 1
module LFSR8_Fib(input clk,input rst, output reg [7:0] lfsr
);always @(posedge clk) beginif(rst)//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便lfsr <= 8'h0; else begin//抽头从1开始为8、6、5、4;增加全1状态的跳出lfsr[0] <= ~(lfsr[3] ^ lfsr[4] ^ lfsr[5] ^ lfsr[7] ^ (lfsr[6:0]==7'b1111111));//低位移动到高位lfsr[7:1] <= lfsr[6:0];end
endendmodule
仿真也没问题,能从全1状态跳出:
同理,伽罗瓦LFSR也可以增加一个判断寄存器是否为全1的电路,并将其输出连接到同或门,示意图如下:
如果不为全1状态,则全1判断电路输出为0,0异或任何数都等于该数本身,即不会对原有电路造成影响。如果为全1状态,则全1判断电路输出为1,此时异或门的输出为0,所以下一个状态的抽头位全变为0,也就是说跳出了全1状态。
这部分的Verilog代码如下:
//8级伽罗瓦LFSR(1到多型LFSR)设计
//同或门作为反馈函数,反馈多项式为 f(x)=x^8 + x^6 + x^5 + x^4 + 1
module LFSR8_Gal(input clk,input rst, output reg [7:0] lfsr
);wire feedback;;//增加全1状态的跳出
assign feedback = lfsr[7] ^ (lfsr[6:0]==7'b1111111);;always @(posedge clk)beginif(rst)//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便lfsr <= 8'h0;else begin//抽头从1开始为8、6、5、4lfsr[0] <= feedback;lfsr[1] <= lfsr[0];lfsr[2] <= lfsr[1];lfsr[3] <= lfsr[2];lfsr[4] <= lfsr[3] ~^ feedback;lfsr[5] <= lfsr[4] ~^ feedback;lfsr[6] <= lfsr[5] ~^ feedback; lfsr[7] <= lfsr[6];end
endendmodule
仿真也没问题,能从全1状态跳出:
- 📣您有任何问题,都可以在评论区和我交流📃!
- 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
- 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!
相关文章:

【FPGA】线性反馈移位寄存器(LFSR)的Verilog实现
什么是移位寄存器 移位寄存器:是指多个寄存器并排相连,前一个寄存器的输出作为下一个寄存器的输入,寄存器中存放的数据在每个时钟周期向左或向右移动一位。 下面的右移移位寄存器因为左侧没有有效输入,所以在第4个时钟周期&…...

yolov8涨点技巧,添加SwinTransformer注意力机制,提升目标检测效果
目录 摘要 SwinTransformer原理 代码实现 YOLOv8详细添加步骤 ymal文件内容 one_swinTrans three_swinTrans 启动命令 完整代码分享 摘要 Swin Transformer通过引入创新的分层注意力机制展现了其架构的独特性,该机制通过将注意力区域划分为块并在这些块内执…...

k8s初始化错误
报错详情: you can check the kubelet logs for further clues by running: ‘journalctl -u kubelet’ Alternatively, there might be issues with your Kubernetes configuration files or maybe the necessary ports are not opened. Check the status of …...
adb命名大全
1. 获取内部版本号: adb shell getprop ro.build.display.innerver 2. 获取按键值: adb shell getevent 3. 获取apk信息: adb shell dumpsys package 包名 ->info.txt 4. 获取应用包名:adb shell dumpsys window windows | gre…...
计算机发展史 (5)携手共赴难
就在痛苦艰难的时刻,孤独苦闷的巴贝奇意外地收到一封来信,写信人不仅 对他表示理解而且还希望与他共同工作。娟秀字体的签名,表明了她不凡的身份 ──伯爵夫人。 接到信函后不久,巴贝奇实验室门口走进来一位年轻的女士。她身披素雅…...

一键搞定简历设计!电子版简历制作指南3步走!
如今,随着无纸化办公趋势的流行,电子简历逐渐取代了纸质简历,成为我们最常用的简历设计格式。 然而,从纸质简历到电子简历后,对于非平面设计领域的学生来说,简历设计的难度可能再次超出了我们的能力范围。…...

tcpdump 常用用法
简要记录下tcpdump用法 监控某个ip上的某个端口的流量 tcpdump -i enp0s25 tcp port 5432 -nn -S 各个参数作用 -i enp0s25 指定抓包的网卡是enp0s25 -nn 显示ip地址和数字端口 ,如果只 -n 则显示ip,但是端口为services文件中的服务名 如果一个…...

Opencv实战(5)平滑处理与常见函数
平滑处理 Opencv实战: Opencv(1)读取与图像操作 Opencv(2)绘图与图像操作 Opencv(3)详解霍夫变换 Opencv(4)详解轮廓 文章目录 平滑处理1.均值滤波2.方框滤波3.高斯滤波4.中值滤波5.双边滤波 常见函数(1).createTrackbar()(2).SetMouseCallback() 图像的平滑处理是…...
音频PCM介绍与运用
音频PCM介绍与运用 什么是PCM PCM (Pulse Code Modulation) 是一种数字音频编码方式,将模拟声音信号转换为数字信号的过程。在 PCM 中,声音信号被采样并量化为离散的数值,以便于数字化处理和传输。 以下是 PCM 的主要特点: 采样…...

计算机专业大学四年应该如何规划(Java方向)
计算机专业的学生,如何在大学四年内提高自己的竞争力,毕业之后直接进大厂工作? 以下将从大学四年计算机专业的学习规划、课程设置、能力提升、参考书籍等方面,为同学们提供一些建议和指导。 大一: 主攻技能学习并且达…...
算法D27|回溯算法4| 93.复原IP地址 78.子集 90.子集II
93.复原IP地址 本期本来是很有难度的,不过 大家做完 分割回文串 之后,本题就容易很多了 题目链接/文章讲解:代码随想录 视频讲解:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址_哔哩哔…...
C++实现XOR加解器
#include <Windows.h> #include <iostream> #include <fstream> #include <string>// 加解密函数,使用XOR运算 void XORCrypt(char* data, int size, const std::string& key) {int keyLength key.length();for (int i 0; i < siz…...

Kubernetes的Sevice管理
服务原理: 所有服务都是根据这个服务衍生或者变化出来,根服务---- 服务感知后端靠标签 slelector 标签选择器 kubectl label pods web1 appweb kubectl cluter-info dump | grep -i service-cluster-ip-range 服务ip取值范围 Service 管理: 创建服务: --- kind: Serv…...

C# 高阶语法 —— Winfrom链接SQL数据库的存储过程
存储过程在应用程序端的使用的优点 1 如果sql语句直接写在客户端,以一个字符串的形式体现的,提示不友好,会导致效率降低 2 sql语句写在客户端,可以利用sql注入进行攻击,为了安全性,可以把sql封装在…...

vue3+vite+ts配置多个代理并解决报404问题
之前配置接口代理总是报404,明明接口地址是对的但还是报是因数写法不对;用了vue2中的写法 pathRewrite改为rewrite 根路径下创建env文件根据自己需要名命 .env.development文件内容 # just a flag ENVdevelopment# static前缀 VITE_APP_PUBLIC_PREFIX"" # 基础模块…...
开创未来:探索OpenAI首个AI视频模型Sora的前沿技术与影响
Sora - 探索AI视频模型的无限可能 随着人工智能技术的飞速发展,AI视频模型已成为科技领域的新热点。而在这个浪潮中,OpenAI推出的首个AI视频模型Sora,以其卓越的性能和前瞻性的技术,引领着AI视频领域的创新发展。让我们将一起探讨…...

Redis---持久化
Redis是内存数据库,是把数据存储在内存中的,但是内存中的数据不是持久的,如果想要做到持久,那么就需要让redis将数据存储到硬盘上。 Redis持久化有两种策略: RDB > Redis DataBase RDB机制采取的是定期备份AOF …...

从 Flask 切到 FastAPI 后,起飞了!
我这几天上手体验 FastAPI,感受到这个框架易用和方便。之前也使用过 Python 中的 Django 和 Flask 作为项目的框架。Django 说实话上手也方便,但是学习起来有点重量级框架的感觉,FastAPI 带给我的直观体验还是很轻便的,本文就会着…...

状态码转文字!!!(表格数字转文字)
1、应用场景:在我们的数据库表中经常会有status这个字段,这个字段经常表示此类商品的状态,例如:0->删除,1->上架,0->下架,等等。 2、我们返回给前端数据时,如果在页面显示0…...

Pytorch 复习总结 4
Pytorch 复习总结,仅供笔者使用,参考教材: 《动手学深度学习》Stanford University: Practical Machine Learning 本文主要内容为:Pytorch 深度学习计算。 本文先介绍了深度学习中自定义层和块的方法,然后介绍了一些…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...