什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch

前言

在FPGA设计中,几乎没人会主动使用锁存器Latch,但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch,而这些Latch可能会给你带来巨大的麻烦。

什么是锁存器Latch?

Latch,锁存器,一种可以存储电路状态信息的组合逻辑元件,和同样可以保存电路状态的时序逻辑元件–触发器(Flip-Flop,FF)不同,锁存器只在其使能端口有效时,将输入传递给输出;而在其使能端口无效时,输出则保持不变,就像被“锁住储存”起来了一样。

下图是一个典型的Latch的门电路结构。当使能信号E无效时,两个与门的输出均为0,对后面的SR锁存器即或非门无影响,所以无论输入D的值为1或0,输出Q的值都不会改变,就像被“锁住”了。
在这里插入图片描述
当使能信号E有效且输入D为1时,两个与门的输出分别为0和1、此时的输出Q值为1,与输入D一致。
在这里插入图片描述
当使能信号E有效且输入D为0时,两个与门的输出分别为1和0、此时的输出Q值为0,同样与输入D一致。
在这里插入图片描述

Latch有什么危害?

当Latch的使能信号有效时,直接让输入信号通过,使得输出完全等于输入。这一特性无疑会很容易地引入毛刺,使得设计不稳定;其次,Latch没有时钟信号,是一个异步电路元件,这就使得综合工具对Latch的静态时序分析十分困难。

而同步逻辑所使用的触发器FF,它的典型结构是这样的,由2个Latch+1个反相器组成。
当时钟信号Clock为高电平时,第1个Latch的使能有效,输入数据直接通过;由于反向器的存在,第2个Latch的使能端无效,所以其输出保持不变。
在这里插入图片描述
当时钟信号Clock为低电平时,第1个Latch使能无效,其输出保持不变,而这个输出也就是在时钟信号处于下降沿时的输入信号;由于反向器的存在,第2个Latch的使能端有效,输入直接传递到输出,此时最终输出的是在时钟下降沿时采集的输入信号。
在这里插入图片描述
可以看到,在同步电路中,FF的输出只在时钟边沿(这里是下降沿)变化,而在其他时间是保持不变的,这样一来,即使某些时刻在输入上出现了毛刺,但只要这个毛刺不发生在时钟边沿,那它就不会传递到输出进而影响后面的电路。

显然,同一个时钟周期比起来,时钟边沿持续的时间实在是微不足道,所以这大大降低了毛刺影响电路的概率。这也是同步电路相对于异步电路的一个大优点。

什么样的代码会生成Latch

下面的一些代码风格是会生成Latch的:

if-else语句不完整的组合逻辑

在组合逻辑中,如果if-else语句没有写完整的话,是会生成Latch的(不管你是有意还是无意的),因为缺失掉的条件会被综合工具理解成不变,即锁存。

module test(input 		data,input 		en,output reg	q
);always@(*)beginif(en)q = data;
endendmodule

Vivado中生成的RTL电路如下–一个Latch。
在这里插入图片描述
生成Latch的时候,Vivado也会报警告,所以设计的时候请务必多多关注这些信息!
在这里插入图片描述
有些时候,尽管你的if-else语句已经很完整了,也还是会生成Latch,就像这样:

module test(input 		data,input 		en,output reg	q
);always@(*)beginif(en)q = data;else	q = q;		//输出反馈给了输入,相当于没变
endendmodule

这是因为输出直接反馈给了输入,使得输出等于输入,相当于没变化,依然是被锁存住了。

消除Latch的方法有两种:

  • 1、补全if-else语句。注意不能将输出反馈给输入,而是应该给输入赋一个其他值,例如0、1或其他变量。
module test(input 		data,input 		en,output reg	q
);always@(*)beginif(en)q = data;else	q = 1'b0;		//q = 1'b1也可以
endendmodule
  • 2、赋初值。在条件语句之前给输出一个初始值。
module test(input 		data,input 		en,output reg	q
);always@(*)beginq = 1'b0;		//q = 1'b1也可以if(en)q = data;
endendmodule

两种方法中推荐第一种方法,因为比较规范,代码风格容易统一。这样的代码不会生成Latch,而是生成一个组合逻辑用的2选1MUX:
在这里插入图片描述

case语句不完整的组合逻辑

与if-else句类似,如果case语句的分支没有列举完全,也是会生成Latch的,比如这样:

module test(input 		data0,data1,input		sel,output reg	q
);always@(*)begincase(sel)1'b0: q = data0;endcase
endendmodule

Vivado中生成的RTL电路会有Latch:
在这里插入图片描述
类似的,有些时候尽管你的case语句已经很完整了,但因为出现了反馈语句(即输出反馈给输入)也还是会生成Latch,就像这样:

module test(input 		data0,data1,input		sel,output reg	q
);always@(*)begincase(sel)1'b0: q = data0;1'b1: q = q;		//反馈语句endcase
endendmodule

消除锁存器的方法有三种:

  • 1、补全case语句。注意不能将输出反馈给输入,而是应该赋一个其他值,例如0、1或其他变量。
module test(input 		data0,data1,input		sel,output reg	q
);always@(*)begincase(sel)1'b0: q = data0;1'b1: q = data1;		endcase
endendmodule
  • 2、赋初值。在case语句之前给输出一个初始值。
module test(input 		data0,data1,input		sel,output reg	q
);always@(*)beginq = data1;	case(sel)1'b0: q = data0;		endcase
endendmodule
  • 3、使用default语句作为默认语句。default语句用来补充其他所有没有列举出来的结果。
module test(input 		data0,data1,input		sel,output reg	q
);always@(*)begincase(sel)1'b0: 		q = data0;default:	q = data1;endcase
endendmodule

在实践中,最推荐第三种写法。风格比较规范和统一。有时候编码的时候可能会把case语句的所有分支都列举出来,这种情况下依然建议保留default语句

这样的代码就不会生成Latch了,而是生成一个组合逻辑用的2选1MUX:
在这里插入图片描述

时序逻辑电路会生成Latch吗?

如果以上出现Latch的写法转换成时序逻辑,是否也会生成Latch?比如这样:

module test(input 		data,input 		en,input		clk,output reg	q
);always@(posedge clk)beginif(en)q <= data;
endendmodule

答案是不会。只会生成一个寄存器reg(实际上就是一个FF)。
在这里插入图片描述
因为在时序逻辑中,即使不补全if-else语句,由于FF/REG的特性,它本身就可以在非时钟边沿存储数据,所以像这样的语句:

else q <= q; //保存值

写或不写在功能上都是一样的。但是在实践中,还是建议补全,目的是养成风格统一、良好的编码习惯。

Latch在FPGA中是什么情况?

网上有种说法:在FPGA设计中不使用Latch的一个很大原因是FPGA中没有现成的Latch资源,如果要用,则需要消耗非常多的资源来构建Latch。

**这种说法是错的。**以Xilinx的7系列FPGA为例,Latch实际上是其内部存在的固定资源。一个Slice中有一半的逻辑资源可以被配置为Latch或FF,另一半只能被配置成FF。但是需要注意的是,如果Silice中的FF被配置成了Latch,那么剩余的4个FF就不能用了,这样确实是会造成资源的浪费。
在这里插入图片描述
Xilinx的7系列FPGA中的FF可以被配置成两种类型的Latch: LDCE和LDPE。

  • 异步复位的LDCE:比我们熟知的Latch多了一个异步复位端口。
    在这里插入图片描述
  • 异步置位的LDPE:比我们熟知的Latch多了一个异步置位端口。

在这里插入图片描述

所以听起来似乎毫无用处的Latch,为什么还存在于FPGA中?

  • 首先Latch是被做成了和部分FF二选一的一个器件,而不是独立器件,这确实也说明在FPGA设计中确实需求不高,不然肯定就做成了FF这种固定的底层器件了。但这样一来,也增加了FPGA底层资源的灵活性,保留了更多的设计可能。

  • 其次,Latch在某些特定应用还是需要的,比如IC设计中的time borrowing来提高时序性能、门控时钟、某些总线接口用来锁存数据等。

总之,Latch的用武之地确实有,但不多。 不是特殊需求,请尽量避免使用Latch!


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

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

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

相关文章

Spring Boot 项目配置文件出现乱码的解决方法

如下图&#xff0c;我们 Spring Boot 项目的配置文件 application.properties 可能会出现如下的乱码问题&#xff1a; 我们写注解的时候是正常的&#xff0c;但是下次启动项目就出现了乱码&#xff0c;这个是字符集设置的问题 解决方法 1.点击 File 选择 Settings 2.搜索 enco…

Grabcut算法在图片分割中的应用

GrabCut算法原理 Grabcut是基于图割(graph cut)实现的图像分割算法&#xff0c;它需要用户输入一个bounding box作为分割目标位置&#xff0c;实现对目标与背景的分离/分割&#xff0c;与KMeans与MeanShift等图像分割方法不同。 Grabcut分割速度快&#xff0c;效果好&#xff0…

stm32 42步进电机 上位机示例

脉冲到底是个啥东西&#xff1f;步进电机一直说发脉冲 步进电机通过接收脉冲信号来实现精确的位置控制。脉冲是一种短暂的电信号&#xff0c;它的变化可以触发步进电机转动一定的角度或步进。步进电机控制系统会根据输入的脉冲信号来精确定位和控制步进电机的转动&#xff0c;每…

YOLOv8 训练自己的分割数据集

之前写过一篇 使用YOLOv8训练自己的【目标检测】数据集-【收集数据集】-【标注数据集】-【划分数据集】-【配置训练环境】-【训练模型】-【评估模型】-【导出模型】&#xff0c;里面带大家整个流程走过一遍了&#xff0c; 这篇文章我们来介绍如何使用 YOLOv8 训练分割数据集&a…

Linux篇:文件系统

一、共识原理&#xff1a; 文件文件内容文件属性 磁盘上存储文件存文件的内容&#xff08;数据块&#xff09;存文件的属性&#xff08;inode&#xff09; Linux的文件在磁盘中存储是将属性和内容分开存储的。 二、硬件简述&#xff1a; 1. 认识硬件 磁盘&#xff1a;唯一的一…

LabVIEW绘制带有多个不同标尺的波形图

LabVIEW绘制带有多个不同标尺的波形图 通过在同一波形图上使用多个轴&#xff0c;可以使用不同的标尺绘制数据。请按照以下步骤操作。 将波形图或图表控件放在前面板上。 1. 右键点击您要创建多个标尺的轴&#xff0c;然后选择复制标尺。例如&#xff0c;如果要为一个…

文件搜索工具HoudahSpot mac中文版特点

HoudahSpot mac是一款文件搜索工具&#xff0c;它可以帮助用户快速准确地找到文件和文件夹&#xff0c;支持高级搜索和过滤&#xff0c;同时提供了多种视图和操作选项&#xff0c;方便用户进行文件管理和整理。 HoudahSpot mac软件特点 高级搜索和过滤功能&#xff1a;软件支持…

手把手教会你--github的学习--持续更新

有什么问题&#xff0c;请尽情问博主&#xff0c;QQ群796141573 前言1.1 使用过程(1) 进入某个项目(2) 点击某个文件(3) 在源码区域下面(4) 源码区的头顶上 1.2 作者的其他项目1.3 搜索1.4 复制别人的代码(即项目)到自己的空间内1.5 上传自己的Bugs(bushi1.6 在线修改文件1.7 评…

【非监督学习 | 聚类】聚类算法类别大全 距离度量单位大全

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

JPA 自关联 设置单向多对一

Spring boot 3 JPA中&#xff0c;遇到一个需求&#xff0c;建一个数据字典表&#xff1a; Dictionary&#xff0c;存放两级数据&#xff0c;第一级为字典项目&#xff0c;第二级为项目内容&#xff0c;查询时要把parent_id对应父项的名称也一起查出来&#xff0c;返回前端。 …

Project DESFT 白皮书中文版——应用于普惠金融的可信数字凭证解决方案

1. 概述 Project DESFT 是由 Solv 基金会与 zCloak Network 联合设计孵化&#xff0c;以跨境贸易和金融服务为场景的分布式可信数字凭证解决方案&#xff08;Distributed Trusted Digital Credential Solution&#xff09;&#xff0c;项目获得新加坡金管局&#xff08;Monetar…

C++学习之路(四)C++ 实现简单的待办事项列表命令行应用 - 示例代码拆分讲解

本期示例介绍: 本期示例《待办事项列表应用》展示了一个简单的任务管理系统&#xff0c;用户可以通过命令行界面执行添加任务、删除任务和显示任务列表等操作。 功能描述&#xff1a; 添加任务功能&#xff1a; 用户可以输入任务描述&#xff0c;将新的任务添加到任务列表中。…

声音响度、声压级计权(A B C)实现

声压 sound pressure 声压就是大气压受到声波扰动后产生的变化&#xff0c;即为大气压强的余压&#xff0c;它相当于在大气压强上的叠加一个声波扰动引起的压强变化。由于声压的测量比较容易实现&#xff0c;通过声压的测量也可以间接求得质点速度等其它物理量&#xff0c;所以…

【开源】基于JAVA的天然气工程业务管理系统

项目编号&#xff1a; S 021 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S021&#xff0c;文末获取源码。} 项目编号&#xff1a;S021&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四…

手把手教会你--渗透实战--Hack The Box-Starting Point-Meow--持续更新

有什么问题&#xff0c;请尽情问博主&#xff0c;QQ群796141573 前言 前言 请务必跟着博主复现一遍 参考&#xff1a; Hack The Box-Starting Point-Meow

pygame加载图像,并让小球做平抛运动

文章目录 load转换和存储自由落体 在游戏设计中&#xff0c;图像显示是必不可少的功能&#xff0c;pygame中的image模块便用于加载图像。 load 通过load函数&#xff0c;可以加载多种图像格式&#xff0c;如下表所示 旧版本bmp, gpeg, png, pcx, tiff, xpmc, lbm(以及pbm, p…

windows下docker环境搭建与运行实战

背景 学习docker使用&#xff0c;需要环境&#xff0c;今天主要的目标是在windows环境下安装docker环境。 为什么要这么搞&#xff0c;主要是企业内部服务器&#xff0c;都是跟公网隔离的&#xff0c;没有访问公网权限&#xff0c;所以镜像什么的&#xff0c;从公网拉取完全没…

卷积神经网络(Inception-ResNet-v2)交通标志识别

文章目录 一、前言二、前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、构建一个tf.data.Dataset1.加载数据2. 配置数据集 三、构建Inception-ResNet-v2网络1.自己搭建2.官方模型 五、设置动态学习率六、训练模型七、模型评…

rust tokio select!宏详解

rust tokio select!宏详解 简介 本文介绍Tokio中select!的用法&#xff0c;重点是使用过程中可能遇到的问题&#xff0c;比如阻塞问题、优先级问题、cancel safe问题。在Tokio 中&#xff0c;select! 是一个宏&#xff0c;用于同时等待多个异步任务&#xff0c;并在其中任意一…

探索 Vue 中的 bus.$emit:实现组件通信的强大工具

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…