什么是好的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
- 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!
相关文章:
什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch
前言 在FPGA设计中,几乎没人会主动使用锁存器Latch,但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch,而这些Latch可能会给你带来巨大的麻烦。 什么是锁存器Latch? Latch,锁存器,一种可以存储电路…...
从0开始学习JavaScript--构建强大的JavaScript图片库
在现代Web开发中,图像是不可或缺的一部分,而构建一个强大的JavaScript图片库能够有效地管理、展示和操作图像,为用户提供更丰富的视觉体验。本文将深入探讨构建JavaScript图片库的实用技巧,并通过丰富的示例代码演示如何实现各种功…...
linux复习笔记05(小滴课堂)
hell脚本与crontab定时器的运用 查看状态: 关闭服务: 开启服务: 重启服务: crontab定时器的使用: 我们可以看到没有任何任务。 编辑: 我们可以看到这个任务了。 删除所有任务: 这代表着每分钟…...
springboot函数式web
1.通常是路由(请求路径)业务 2.函数式web:路由和业务分离 一个configure类 配置bean 路由等 实现业务逻辑 这样实现了业务和路由的分离...
常见的1/2/3位数码管接线详解
今天玩数码管的时候接触到了数码管的接线,分享一下供刚开始接触的童鞋参考 首先了解什么是数码管 数码管是一种可以显示数字和其他信息的电子设备,是显示屏其中一类, 通过对其不同的管脚输入相对的电流,会使其发亮,从而…...
C++模板介绍
定义 C模板是一种编程技术,它允许程序员在编译时生成具有特定类型的函数或类,而无需在运行时进行类型检查。模板是一种泛型编程的方式,它使得程序员可以编写可适用于多种数据类型的代码,提高了代码的重用性和灵活性。 C模板可以…...
kafka kraft 集群搭建保姆级教学 包含几个踩坑点
一.为啥弃用zookeeper kafka 弃用 ZooKeeper 而采用 KRaft 的主要原因是为了改进 Kafka 集群的可靠性和可管理性。 在传统的 Kafka 架构中,ZooKeeper 用于存储和管理集群的元数据、配置信息和状态。然而,使用 ZooKeeper 作为协调服务存在一些限制和挑战…...
html实现360度产品预览(附源码)
文章目录 1.设计来源1.1 拖动汽车产品旋转1.2 汽车产品自动控制 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/134613931 html实现360度产品预览(附源码&…...
11-23 SSM4
Ajax 同步请求 :全局刷新的方式 -> synchronous请求 客户端发一个请求,服务器响应之后你客户端才能继续后续操作,请求二响应完之后才能发送后续的请求,依次类推 有点:服务器负载较小,但是由于服务器相应…...
CPU、GPU、TPU内存子系统架构
文章目录 CPU、GPU、TPU内存子系统架构概要CPUGPUTPU共同点和差异: CPU、GPU、TPU内存子系统架构 概要 Memory Subsystem Architecture,图源自TVM CPU CPU(中央处理器)的内存子系统:隐式管理 主内存(…...
R数据分析:集成学习方法之随机生存森林的原理和做法,实例解析
很久很久以前给大家写过决策树,非常简单明了的算法。今天给大家写随机(生存)森林,随机森林是集成了很多个决策数的集成模型。像随机森林这样将很多个基本学习器集合起来形成一个更加强大的学习器的这么一种集成思想还是非常好的。…...
transformers pipeline出现ConnectionResetError的解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
代码随想录-刷题第九天
28. 找出字符串中第一个匹配项的下标 题目链接:28. 找出字符串中第一个匹配项的下标 思路1:先来写一下暴力解法。 时间复杂度O(n*m) class Solution {public int strStr(String haystack, String needle) {// 暴力解法先来一遍for (int i 0; i <…...
MySQL基本SQL语句(下)
MySQL基本SQL语句(下) 一、扩展常见的数据类型 1、回顾数据表的创建语法 基本语法: mysql> create table 数据表名称(字段名称1 字段类型 字段约束,字段名称2 字段类型 字段约束,...primary key(主键字段 > 不能为空、必须唯一) ) …...
【洛谷算法题】P5715-三位数排序【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5715-三位数排序【入门2分支结构】🌏题目描述🌏输入格式…...
上海亚商投顾:北证50指数大涨 逾百只北交所个股涨超10%
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 沪指11月24日震荡调整,深成指、创业板指盘中跌超1%。北证50指数大涨超6%,北交所个股持…...
设计模式—依赖倒置原则(DIP)
1.概念 依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。 通俗的讲࿱…...
windows下docker环境搭建与运行实战
背景 学习docker使用,需要环境,今天主要的目标是在windows环境下安装docker环境。 为什么要这么搞,主要是企业内部服务器,都是跟公网隔离的,没有访问公网权限,所以镜像什么的,从公网拉取完全没…...
c# EF框架的增删改查操作
查询 /// <summary>/// 查询/// </summary>private void SQLLoad(){//linq查询,方法一//var UserInfoList from u in db.UserInfo//给个变量u,用来接收查询结果对象//select u;//查询结果对象 //db.UserInfo.Find(1);//依据主键查询,方法二…...
Element-UI Upload 手动上传文件的实现与优化
文章目录 引言第一部分:Element-UI Upload 基本用法1.1 安装 Element-UI1.2 使用 <el-upload> 组件 第二部分:手动上传文件2.1 手动触发上传2.2 手动上传时的文件处理 第三部分:性能优化3.1 并发上传3.2 文件上传限制 结语 Ἰ…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
