完整教程:Verilog和FPGA的自学笔记6——计数器(D触发器同步+异步方案)

news/2025/11/13 20:57:57/文章来源:https://www.cnblogs.com/yangykaifa/p/19219538

先扯点别的,也是填填前面的坑,比如……

阻塞?非阻塞?

一直都不知道把这个写在哪里,随便安排一下叭(捂脸……)
Verilog中的赋值类型分为阻塞赋值(blocking)和非阻塞赋值(Non-Blocking)。

关于在一个名词后面加一个英文词汇这件事,虽然不知道有多少人会认真看,但我还是要加,因为这样可以显得我英语很好哈哈~~

对于阻塞赋值,嗯……看名字应该是什么阻塞了赋值。

  • 语法结构:变量 = 表达式;

做个阅读程序写结果的题吧!cos信息学初赛试题……

认真思考!

reg a,b,c;
always @(*) begin
a = 1'b1;
b = a;
c = a + b;
end

各位编程大佬发表发表意见,说说a,b,c分别等于多少?

以上就是一段经典的三个阻塞赋值语句。
结果:a=1,b=1,c=0。(盲猜会阻塞赋值的也会做错哈哈 ^_^,别问我怎么知道的……捂脸)

关于Verilog,大家都知道一句很经典的名言:

不要用软件编程的思维思考硬件编程

哎,但这里是顺序执行的:先把1赋给a,再把等于1的a赋给b,最后计算1+1 = 10 ,由于c为一位寄存器,丢掉高位后为0;

所以,always语块中阻塞赋值是串行执行的

此乃阻塞赋值之真谛。

非阻塞赋值就好理解了,核心就是FPGA的精华:并行处理。

  • 语法结构:变量 <= 表达式; (区别就在有没有小于号)

重做一遍(只考虑第一次运行):

reg a,b,c;
assign a = 1'b0;
assign b = 1'b0;
assign c = 1'b0;     //初始化信号为0
always @(*) begin
a <= 1'b1;
b <= a;
c <= a+b;
end

结果:a = 1,b = 0,c = 0。
第一次可能有点迷糊,过程是这样的:首先,所有非阻塞语句右边表达式同时进行运算。

那第二、三个语句a和b还没算出来怎么办呢?

答:按照之前的旧数算(a=0,b=0)。(这里就把右边的ab看成具体的数,不要看成变量)

之后将结果同时赋给变量a,b,c。

所以最终结果分别是1,0,0。

注意:阻塞和非阻塞是对于always来说的,assign只有"="赋值。
别把下面这个东西写出来!

assign a <= b;

异步计数器

我觉得计数器中最好理解的当属异步计数器,我们就先做这个简单的吧~
个人观点:同步计数器纯粹锻炼思维用的……

D触发器构成的异步二进制计数器
CP 是计数器的时钟信号,每当脉冲到来时对D(Data)上的信号进行寄存。无论默认状态下Q非如何,由于Q非连接D,所以在每个脉冲下进行反转,也就是二分频。

同理,再把第一个D触发器当作时钟输入第二个D,对信号再次分频得到四分频。这同时也是一个两位二进制计数器。

原理说完了,接下来我们就要思考实现了。
鉴于我和大家都已经对Verilog有了the first impression,今天按照FPGA工程流程来吧!

1.模块设计

要设计一个module,我们首先要明确其具体功能,以及对应的输入和输出信号。
对于异步二进制计数器,时钟(CLK)是必不可少的,除此之外还应有复位(RST)信号。输出则是计数器的每一位(假设3位)。

这样我们就得到了计数器模块的大体框架:

在这里插入图片描述

自己画的,知道不好看,将就着看吧……

把时钟信号和复位信号分别命名为sys_clk和sys_rst是行业习惯,咱也跟着写吧。

这里注意输出的out信号应为3位寄存器类型。

2.RTL代码

给大家讲个笑话:昨天本人花了好一阵子才把异步计数器写出来……但写完后真是觉得自己对Verilog代码的应用更加得心应手了。说句实话,如果你确实认真看完前五篇笔记,那么现在能力上对于完成计数器是绰绰有余了。真心希望大家下定决心无论如何也要自己把它写出来,最好直接仿真通过,再和我提供的代码进行对比。这样的进步绝对比先看代码再自己写要大得多。

大家一定相信我啊!

无从下手时的一点提示:考虑一下always的时序逻辑。

module counter(
input sys_clk,
input sys_rst,
output [2:0] out
);
reg reg1,reg2,reg3;
always @(posedge sys_clk or negedge sys_rst) begin
if (!sys_rst)
reg1 <= 1'b0;
else
reg1 <= ~reg1;
end
always @(negedge reg1 or negedge sys_rst) begin
if (!sys_rst)
reg2 <= 1'b0;
else
reg2 <= ~reg2;
end
always @(negedge reg2 or negedge sys_rst) begin
if (!sys_rst)
reg3 <= 1'b0;
else
reg3 <= ~reg3;
end
assign out = {reg3,reg2,reg1};
endmodule

其实写完了看着吧真没啥,就是写了仨反转机而已,但一开始设计就是不知道咋写……捂脸。

下面是vivado综合出来的硬件电路:
在这里插入图片描述有点混乱……
不过可以看到对应FPGA的D触发器并没有Q非,而是在Q后面接一个反相器实现的。

一开始很久没看明白这破图里粗绿线还有黑三角啥意思,半天才看明白那是表示总线,总线上对应数字相连。黑三角代表总线与单根线的连接。(破涕为笑)

3.编写仿真文件

这次关于仿真文件说个新知识点(不算新吧,反正之前没这么用过):怎么实现CLK信号的连续反转捏?总不能无限的复制粘贴吧~

其实你只要复制粘贴一个周期(毕竟一个周期对了就行)
虽然理论可行,但总感觉差点意思。

确实,所以这里给大家介绍always语句的新用法,专门在tb里的用法:

always #延迟时间 语句;

这意思就是说always间隔一段延时时间,就执行一次后面的语句。

比如上面计数器的RTL代码对应的tb文件中的CLK信号就应该这么写(故意写这么长的句子哈哈):

always #10 sys_clk <= ~sys_clk;

这意思就是每隔10个时间单位(不见得一定是1ns),sys_clk信号就反转一次。
时钟信号就这么完美的完成啦!开心~

下面是我写的tb文件:

`timescale 1ns/1ns
module tb_register();
reg sys_clk,sys_rst;
wire [2:0] out;
initial begin
sys_clk = 1'b0;
sys_rst = 1'b0;
#100
sys_rst = 1'b1;
end
always #10 sys_clk <= ~sys_clk;
register u_register(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.out(out)
);
endmodule

以下是我仿真出来的图,左边有对应名称:

在这里插入图片描述

相信大家都能自己走到这一步!

同步二进制计数器

接下来的电路会有一(亿?)点点小复杂……

在这里插入图片描述
这是一个经典的由D触发器组成的同步二进制计数器。同步和异步的区别就在于时钟信号:所有触发器的时钟相同称为同步,反之亦然。

上图中G1、G2为异或门(XOR),G3为与门(AND)。

第一个D触发器很好理解,就是一个毫无特色的1位计数器。

为方便大家理解,真值表列举如下(假设三个触发器初始值为0):

clkQ0
00
11
20
31

接下来看第二个,其数据输入端为F1和它本身的异或。
第1个时钟到来时,Q0和Q1均为0,异或结果为0,F2寄存值为0.
第2个时钟到来时,Q0为1,Q1为0,异或结果为1,F2寄存值为1.
第3个时钟到来时,Q0为0,Q1为1,异或结果为1,F2寄存值为1.

列出真值表如下:

clkQ0Q1
000
110
201
311

可以看到,这个电路通过利用异或门非常巧妙的实现了二分频 (话说这是哪个天才想到的……)

既然可以通过异或门实现分频,那对于F3为什么又用了与门呢?咱们不妨再用异或门构尝试一下:若仅将Q2与Q3的异或结果输入F3,结果如何?

第1个时钟到来时,Q1和Q2均为0,异或结果为0,F3寄存值为0.
第2个时钟到来时,Q1为0,Q2为0,异或结果为0,F3寄存值为0.
第3个时钟到来时,Q1为1,Q1为0,异或结果为1,F3寄存值为1.
第4个时钟到来时,Q1为1,Q1为1,异或结果为0,F3寄存值为0.

真值表如下:

clkQ1Q2
000
100
210
311
400
500
610
711

可以看到,仅通过相同异或是无法对Q1再分频的。再准确点,Q1与Q2的周期是相同的(同为4个clk)。

那咋办捏?

我们再观察Q1和Q2的真值表,导致第四个clk到来时Q2反转的原因是Q1和Q2相同(同为1),然而之后的三个时钟周期Q2总是与Q1相同。所以,如果我们能够让Q2在第四个clk时把1保留下来,这事不就妥了吗?

那如何保持第四个clk时Q2的输出不变呢?我们现在在设计时序电路,对于时序电路最重要的就是观察利用每个时钟下状态。所以再回去瞅瞅Q0和Q1在第四个clk附近的真值表。

发现当第三个clk结束时Q0和Q1同为1。或者说,如果给Q0和Q1按上个与门,结果与Q1相同(即不影响Q1的结果)

所以如果我们将这个与门的输出接入F3,那么Q2的1就可以维持到第五个clk的到来。
再之后,由于Q2为1,它将与Q1总是不同,此时再利用异或门就可以持续维持1了。

clkQ0Q1Q2
0000
1100
2010
3110
4001
5101
6011
7111

在这里,我们巧妙的利用了时钟周期,并通过与门实现了Q2在第四个clk结束时状态的维持。

不过……
其实我现在也是在对照答案讲题而已(捂脸x3),至于D触发器的同步二进制计数器的设计逻辑至今也不知道,恳请路过的大佬给出指教(抱拳x3)……

接下来就可以开始编写RTL代码了,按照电路图写就号好:
还是希望大家先自己写……

module counter(
input sys_clk,
input sys_rst,
output [2:0] out
);
reg F1, F2, F3;
// 时序逻辑,在时钟边沿更新所有寄存器
always @(posedge sys_clk or negedge sys_rst) begin
if(!sys_rst) begin
// 复位时清零所有寄存器
F1 <= 1'b0;
F2 <= 1'b0;
F3 <= 1'b0;
end
else begin
// 每个时钟周期更新值,使用前一个状态计算新值
F1 <= ~F1;                  // F1 翻转
F2 <= F1 ^ F2;              // F2 基于 F1 和自身前值更新
F3 <= (F1 & F2) ^ F3;       // F3 基于 F1、F2 和自身前值更新
end
end
// 输出组合
assign out = {F3, F2, F1};
endmodule

仿真文件与异步计数器的完全相同(毕竟除了原理,两个计数器的工作流程完全一致),就不再水字数啦哈哈~

仿真出来的波形也完全一样,就不再贴图啦~

这两天常常思考同步和异步计数器再设计逻辑上的区别,这里浅谈一下个人感受。

异步计数器很简单,更像是无脑堆触发器。对于同步计数器而言,无论是时序还是逻辑完全上了一个档次。之前疑惑为啥非要整一个同步的呢?功能没区别逻辑却复杂了不少。但是FPGA之所以在算法上能够比微控制器快很多,就在于它没有复杂的机器周期,它几乎在一个晶振周期中完成全部算法。而同步计数器,从这个角度说,貌似更符合FPGA的设计本质。

还有这种方式?!

其实,计数器的设计完全不用这么复杂的……
(难道我们在C++里的计数器需要复杂的逻辑运算吗?)

看看这个:

module counter(
input	sys_clk,
input	sys_rst,
output	reg [2:0]	cnt
);
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
end
endmodule

哈哈~

如果有不明白或错误之处,也希望大家在评论区给出,帮助大家的同时也能再次提升自己对于FPGA和Verilog的理解,感谢大家!!

系列链接:
上一篇:Verilog和FPGA的自学笔记5——三八译码器(case语句与锁存器)
下一篇:码字ing……

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

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

相关文章

LucaOne架构

https://github1s.com/lucaone/LucaOne/blob/master/src/models/lucaone_gplm.py LucaOne模型架构详解 LucaOne是一个统一的基因-蛋白质多模态基础模型,其架构设计体现了对生物序列本质的深刻理解。模型的核心目标是打…

实用指南:Windows安装MongoDB保姆级教程(图文详解)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

linux USB --- 监听 USB 角色

#include <linux/device.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/gpio/consumer.h> #include <linux/platform_device.h> #include <linux/usb/typ…

温州工友自动包装设备有限公司:专注螺丝五金智能包装,助力企业降本增效

在温州乐清电器之都,有一家深耕螺丝五金自动化包装设备领域多年的企业 —— 温州工友自动包装设备有限公司。该公司技术研发始于 2007 年,经过多年积累与沉淀,于 2011 年正式注册成立,如今已发展成为集研发生产、销…

25.11.09

CF1935F 如果代价都能做到 1,那就再好不过了。 尝试一下,发现一个子树总有最小值 \(x\),那么连接 \((x,x-1)\) 看起来就做完了。 但是可能 \(x-1\) 被删了,这样就挂了,需要连 \((x,x-2)\)。 然后发现 \((x,x-2)\)…

NOI2025 游记

Day -? UNR。两场应该都认真打了。但是一题不会,打了六个暴力喜提 rk 130 Ag。事后看来这可能掉了 RP。 D2T1 订了一下午。我怎么一点不会数数。 Day -1 报到日。 徽章订了不少,但是我真的太社恐了。跟着 winston 去…

NOIP 考前做题计划

11月14号 复习蓝书上的 \(0x06\) 和 \(0x07\) 节,重新思考 \(0x08\) 内这两节的习题,并为代码写注释 11月15号 上午打洛谷模拟赛,晚上复习蓝书上 \(0x11\) 节,并做当节习题以及 \(0x18\) 节对应的习题 11月16号 复…

网络攻防实战 lab06 靶机 VulnHub hard-socnet2

地址发现 arp-scan -l得到地址为 10.0.2.8。 端口发现与服务版本探测 nmap -p- nmap -p22,80 -sC -sV

[豪の学习笔记] Spring框架学习碎碎念#5

主动获取Bean对象、Bean的作用域、第三方Bean管理跟学视频:黑马JavaWeb课程 Bean管理 获取Bean ​ 默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将b…

Docker部署Code-Server,实现远程写代码

Docker部署Code-Server,实现远你不需要在自己的电脑上安装任何复杂的工具,只需要一个浏览器,就可以开始 C、C++ 和 Python 的学习与开发。参考:使用 Docker 部署 VS Code in The Browser - 谱次 - 博客园 hub.dock…

2025 年 11 月电力金具厂家最新推荐,精准检测与稳定性能深度解析!

全球电力系统对核心部件稳定性的依赖度持续攀升,电力金具的质量与性能直接影响输电网络安全。本次 2025 年 11 月电力金具厂家推荐榜单,由国际电力设备检测协会(IPTA)联合全球三大电力工程机构共同发布,测评覆盖全…

2025 年 11 月铁附件厂家最新推荐,聚焦资质、案例、售后的五家企业深度解读!

随着电力工程、光伏发电及基础设施建设领域的全球化推进,铁附件作为核心配套产品,其品质与供应效率对工程质量的影响愈发显著。为精准筛选优质铁附件厂家,本次推荐结合国际电力设备协会(IEPEA)2025 年第三季度测评…

LucaOne模型的词汇表系统

https://github1s.com/lucaone/LucaOne/blob/master/src/models/alphabet.py#L1-L205 通过定义词汇表,就能输入蛋白质或者DNA。 这段代码定义了 LucaOne模型的词汇表系统(Alphabet类),是模型处理不同生物分子序列的核…

v4l2用户侧使用流程

前言 v4l2的用户侧使用方法的简单demo,参考:media/v4l/v4l2grab.c 实际上,v4l2的用户侧有libv4l开源库。用户可以使用此开源库快速搭建其用户侧代码。 流程 int main() {// openfd = v4l2_open("/dev/video0&q…

2025 年终端数据安全软件公司推荐数篷科技(深圳)有限公司,数据安全领域的坚实力量

在数字化浪潮席卷全球的当下,数据已然成为企业的核心资产,其安全防护至关重要。随着 AI、云计算等新兴技术的广泛应用,数据安全面临着前所未有的挑战,如数据泄露、非法访问等风险日益加剧。在此背景下,一款可靠的…

Day37(7)-F:\硕士阶段\Java\课程代码\后端\web-ai-code\web-ai-project01\springboot-web-01

HTTP状态码大全 https://cloud.tencent.com/developer/chapter/13553package com.itheima;import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.ResponseEntity; import org.springfra…

网络协议工程 - eNSP及相关软件安装 - [eNSP, VirtualBox, WinPcap, Wireshark, Win7] - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

20232314 2025-2026-1 《网络与系统攻防技术》实验五实验报告

一、实验内容从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取信息。 尝试获取BBS、论坛、QQ、MSN中某一好友的IP地址,并查询获取该好友所在的具体地理位置。 使用nmap开源软件对靶机环境…

深度学习实验一之图像特征提取和深度学习训练数据标注 - 实践

深度学习实验一之图像特征提取和深度学习训练数据标注 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Cons…

题解:ABC232G Modulo Shortest Path

由于 \(\forall i,a_i < m,b_i < m\),所以可能的边权要么是 \(a_i+b_j\),要么是 \(a_i+b_j-m\),下文简称其为一类边和二类边。 暴力建图太浪费了,发现与一个 \(a_i\) 的连边是二类边的 \(b_j\) 的值单调不减…