【FPGA】线性反馈移位寄存器(LFSR)的Verilog实现

news2024/9/19 7:49:42

什么是移位寄存器

移位寄存器:是指多个寄存器并排相连,前一个寄存器的输出作为下一个寄存器的输入,寄存器中存放的数据在每个时钟周期向左或向右移动一位。

下面的右移移位寄存器因为左侧没有有效输入,所以在第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) begin
	if(rst)
		//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便
		lfsr <= 8'h0;	
	else begin
		//抽头从1开始为8、6、5、4
		lfsr[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 begin
    clk	= 1'b1;
	forever #5 clk = ~clk;
end

//生成复位信号
initial begin
		rst = 1'b1;	//复位
    #45 rst = 1'b0; //取消复位
end

//仿真过程
initial begin
	wait(cnt == 255);	//所有状态都仿真结束
    #10 $stop;			//关闭仿真
end

//记录状态个数,每个有效时钟周期加1
always@(posedge clk)begin
	if(rst)
		cnt <= 8'd0;
	else begin
		cnt <= cnt + 1'd1;
	end
end

endmodule

仿真结果如下:
在这里插入图片描述
与正确向量一一对比即可,发现仿真结果无误。
在这里插入图片描述

方法2:将仿真结果打印到Tcl窗口,然后复制到文件,再与正确向量对比。对比方式可以是手动对比,也可以是用插件自动进行对比。

这种方法只需要部分修改TB文件:

//记录状态个数,每个有效时钟周期加1
always@(posedge clk)begin
	if(rst)
		cnt <= 8'd0;
	else begin
		cnt <= 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 begin
    clk	= 1'b1;
	forever #5 clk = ~clk;
end

//生成复位信号
initial begin
		rst = 1'b1;	//复位
    #45 rst = 1'b0; //取消复位
end

//仿真过程
initial begin
	wait(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)begin
	if(rst)
		cnt <= 8'd0;
	else begin
		cnt <= cnt + 1'd1;
		//$display("%h",lfsr);	//打印每一个状态到窗口
		//打印每一个状态到文件
		$fdisplay(handle_file_out,"%h",lfsr);	
	end
end

endmodule

打开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 begin
    clk	= 1'b1;
	forever #5 clk = ~clk;
end

//生成复位信号
initial begin
		rst = 1'b1;	//复位
    #45 rst = 1'b0; //取消复位
end

//读取正确向量
initial begin
	$readmemh("G:/file_in.txt",lfsr_gold);	//绝对路径
end

//仿真过程
initial begin
	wait(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)begin
	if(rst)begin
		cnt <= 8'd0;
		flag <= 1'b0;
		cnt_error <= 8'd0;
	end	
	else begin
		cnt <= cnt + 1'd1;
		if(lfsr_gold[cnt] != lfsr)begin	//如果对比有误
			//$display("cnt=%d is wrong",cnt);	//打印错误的地方
			flag <= 1'b1;				//拉高错误标志
			cnt_error <= cnt_error + 1;	//错误计数器加1
		end
		else begin
			flag <= 1'b0;
			cnt_error <= cnt_error;
		end	
	end
end

endmodule

为了对比仿真结果,我故意把正确向量的第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)begin
	if(rst)
		//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便
		lfsr <= 8'h0;
	else begin
		//抽头从1开始为8、6、5、4
		lfsr[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	
end

endmodule

电路示意图如下:
在这里插入图片描述
仿真脚本依然用上面的即可,这里就不啰嗦了。

禁止状态的处理

用同或门作为反馈函数的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) begin
	if(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
end

endmodule

仿真也没问题,能从全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)begin
	if(rst)
		//同或门种子可以选取全0,同时FPGA复位后也会复位到0,比较方便
		lfsr <= 8'h0;
	else begin
		//抽头从1开始为8、6、5、4
		lfsr[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	
end

endmodule

仿真也没问题,能从全1状态跳出:
在这里插入图片描述


  • 📣您有任何问题,都可以在评论区和我交流📃!
  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1471536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

转本考前如何调整心态

不少同学还在过年的氛围中还没走出来。 担忧自己成绩不进反退&#xff0c;又不知道该如何调整心态&#xff01;这个时候小编就有几点小建议给到各位考生。 *心态*情绪 良好的考试心态是没有固定的心态&#xff0c;对不同学习情况的学生来说&#xff0c;良好的考试心态是不一…

微信小程序--怎样在小程序中创建地图并渲染数据中的点标记

效果&#xff1a; 首先--创建地图 使用官方文档中的地图组件 map <map id"mapId" class"map" longitude"{{longitude}}" latitude"{{latitude}}" markers"{{markers}}"></map> 其中的属性值&#xff1a; lon…

爬虫入门四(抽屉半自动点赞、xpath使用、动作链、打码平台、scrapy框架介绍与安装及创建项目)

文章目录 一、抽屉半自动点赞二、xpath的使用三、动作链四、打码平台介绍超级鹰打码基本测试 五、自动登录超级鹰六、scrapy框架介绍安装创建爬虫项目 一、抽屉半自动点赞 登录抽屉账号保存cookiesimport timeimport jsonfrom selenium import webdriverfrom selenium.webdrive…

使用向量数据库pinecone构建应用04:混合搜索 Hybrid Search

Building Applications with Vector Databases 下面是这门课的学习笔记&#xff1a;https://www.deeplearning.ai/short-courses/building-applications-vector-databases/ Learn to create six exciting applications of vector databases and implement them using Pinecon…

啤酒:精酿啤酒与烧烤的热烈碰撞

在夏日的傍晚&#xff0c;烧烤与啤酒总是绝配。当Fendi Club啤酒遇上烧烤&#xff0c;它们将为我们带来一场热烈的美味碰撞。 Fendi Club啤酒&#xff0c;以其醇厚的口感和淡淡的麦芽香气而著称。这款啤酒在酿造过程中采用了特别的工艺&#xff0c;使得酒体呈现出诱人的金黄色&…

【JVM】线上一次fullGC排查思路

fullGC问题背景 监控告警发现&#xff0c;今天开始我们线上应用频繁出现fullGC&#xff0c;并且每次出现后磁盘都会被占满 查看监控 查看监控发现FULLGC的机器均为同一个机房的集器&#xff0c;并且该机房有线上error报错&#xff0c;数据库监控对应的时间点也有异常&#x…

如何在Linux搭建MinIO服务并实现无公网ip远程访问内网管理界面

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

Linux系统中前后端分离项目部署指南

目录 一.nginx安装以及字启动 解压nginx 一键安装4个依赖 安装nginx 启动 nginx 服务 开放端口号 并且在外部访问 设置nginx自启动 二.配置负载均衡 1.配置一个tomact 修改端口号 8081端口号 2.配置负载均衡 ​编辑 三.部署前后端分离项目 1.项目部署后端 ​编辑…

上海亚商投顾:沪指8连阳重新站上3000点 全市场逾百股涨停

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指2月23日继续反弹&#xff0c;成功收复3000点大关&#xff0c;录得8连阳走势。AI概念持续活跃&#xff0c…

springboot219基于SpringBoot的网络海鲜市场系统的设计与实现

网络海鲜市场系统的设计与实现 摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&…

日志系统项目(2)项目实现(实用工具类、日志等级类、日志消息类、日志格式化输出类)

前面的文章中我们讲述了日志系统项目的前置知识点&#xff0c;再本文中我们将开始日志项目的细节实现。 日志系统框架设计 本项目实现的是一个多日志器日志系统&#xff0c;主要实现的功能是让程序员能够轻松的将程序运行日志信息落地到指定的位置&#xff0c;且支持同步与异…

20240223-2092.查找所有有秘密的人

题目要求 给你一个整数 n&#xff0c;表示有 n 个人&#xff0c;编号从 0 到 n - 1。你还给你一个 0 索引的二维整数数组 meetings&#xff0c;其中 meetings[i] [xi, yi, timei] 表示 xi 和 yi 在 timei 有一个会议。一个人可以同时参加多个会议。最后&#xff0c;给你一个整…

【Flutter/Android】运行到安卓手机上一直卡在 Running Gradle task ‘assembleDebug‘... 的终极解决办法

方法步骤简要 查看你的Flutter项目需要什么版本的 Gradle 插件&#xff1a; 下载这个插件&#xff1a; 方法一&#xff1a;浏览器输入&#xff1a;https://services.gradle.org/distributions/gradle-7.6.3-all.zip 方法二&#xff1a;去Gradle官网找对应的版本&#xff1a;h…

个人机器人课程中最棘手的问题

爱好 22年&#xff0c;观点。当然现在自动驾驶研究路径已经不是22年的模式了。 23年&#xff0c;观点&#xff1a; 一个热爱自动驾驶但妥妥外行之人的思考-2023-CSDN博客 前篇 不合格机器人工程讲师为何不分享成功的案例-CSDN博客 在这篇文章中&#xff0c;有一段&#x…

Maya笔记 设置工作目录

Maya会把素材场景等自动保存在工作目录里&#xff0c;我们可以自己定义工作目录 步骤1 创建workspace.mel文件 文件/设置项目 ——>选择一个文件夹&#xff0c;点击设置——>创建默认工作区 这一个后&#xff0c;可以在文件夹里看到.mel文件 步骤2 自动创建文件夹…

进程的学习

进程基本概念: 1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态查看当前系统中的所有进程信息&#xff08;根据CPU占用率排序&#xf…

python中那些双下划线开头得函数和变量

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 Python中下划线—完全解读 Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用from module import *导入 __xxx__ 系统定义名字 __xxx 类中的私有变量…

PC蓝牙通信

一、基本概念 蓝牙用于不同设备之间建立联系。利用无线电波在短距离间发送数据&#xff0c;wifi是在路由器跟设备间传输数据。蓝牙是在设备之间。蓝牙跟wifi同在2.4GHz频率下工作。蓝牙信号比wifi信号弱很多功率仅为1mW。传输距离有限。最初的1.0版本传输距离只有10m。现在5.0…

快速启动-后台管理系统

目录 Gitee人人开源 后端快速启动 1.clone仓库到本地 2.初始化数据库 3.更改数据库连接 4.启动项目验证 前端快速启动 1.克隆仓库 2.vsCode打开 3.控制台npm install 4.验证测试 时代已然不同&#xff0c;后台管理也可以使用脚手架方式快速启动。 Gitee人人开源 地…

JavaWeb——006MYSQL(DDLDML)

这里写目录标题 数据库开发-MySQL首先来了解一下什么是数据库。1. MySQL概述1.1 安装1.1.1 版本1.1.2 安装1.1.3 连接1.1.4 企业使用方式(了解) 1.2 数据模型1.3 SQL简介1.3.1 SQL通用语法1.3.2 分类 2. 数据库设计-DDL2.1 项目开发流程2.2 数据库操作2.2.1 查询数据库2.2.2 创…