在学习FPGA使用Verilog HDL语言编程时,开始遇到时序逻辑和组合逻辑时概念一看就明白,但是实际使用时还是不清楚到底要用哪个。现在用就一个例子来体会一下这两者的区别。

首先先看组合逻辑和时序逻辑的定义。

看完以后还是感觉云里雾里搞不清楚,那么就不用管它了,直接用例子来说明。

在这里设计一个0---9计数器,clk为输入时钟信号,cin为计数有效信号,也就是说只有当cin为高电平时,计数器才计数一次。cout为计数进位信号,当计数值为9时,计数值再加1的话,就输出一个进位信号,同时计数值清零。q输出计数值,输出值的范围是0--9。这个计数器类似于数码管显示数字时每一个数码管的显示范围,每个数码管显示范围为0--9,当低位满10之后,向前一位进1,同时低位清零。

下面开始编写代码

首先定义输入输出端口

module bcd_counter(input   clk,        //时钟input   rst_n,      //复位input   cin,        //计数使能output  cout,       //进位输出output [3:0] q      //计数输出
);
endmodule

输入信号有三个 时钟 clk、复位 rst_n、计数使能 cin。

输出信号有两个 进位输出cout、计数值输出q,q的计数范围是0--9,所以q设置为4位计数器。

下面编写计数代码

reg [3:0] cnt;
//BCD码计数
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cnt <= 4'd0;else if(cin == 1'b1) begin      //计数使能信号有效时 计数器加1if(cnt < 4'd9)cnt <= cnt + 1'b1;elsecnt <= 4'd0;endelse cnt <= cnt;
end

cnt存储计数值,复位后默认值为0,每次当cin为高电平时,计数值加1,当计数到9时,计数值清零。cin为低电平时,计数值保持不变。这样计数寄存器的值就在0-到9之间循环。

下面编写进位代码

always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cout <= 1'b0;else if(cnt == 4'd9 && cin == 1'b1)cout <= 1'b1;elsecout <= 1'b0;
end

当计数寄存器的值为9,同时计数使能信号为1时,说明已经计够10次了,需要进位一次,这时cout输出1。其余情况下输出为0。

最后将寄存器的值连接到输出端口上

assign q = cnt; 

整体代码如下

module bcd_counter(input   clk,        //时钟input   rst_n,      //复位input   cin,        //计数使能output  cout,       //进位输出output [3:0] q      //计数输出
);reg [3:0] cnt;
//BCD码计数
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cnt <= 4'd0;else if(cin == 1'b1) begin      //计数使能信号有效时 计数器加1if(cnt < 4'd9)cnt <= cnt + 1'b1;elsecnt <= 4'd0;endelse cnt <= cnt;
end
//进位信号输出
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cout <= 1'b0;else if(cnt == 4'd9 && cin == 1'b1)cout <= 1'b1;elsecout <= 1'b0;
endassign q = cnt;        //输出计数值endmodule

代码的功能比较简单,一个always语句产生计数信号,一个always语句产生进位信号。

下面编写测试文件

`timescale 1ns/1ns
module bcd_counter_tb;
parameter T = 20;reg sys_clk;
reg sys_rst_n;
reg cin;wire cout;
wire [3:0] q;bcd_counter bcd_counter0(.clk        (sys_clk),        //时钟.rst_n      (sys_rst_n),      //复位.cin        (cin),            //计数使能.cout       (cout),           //进位输出.q          (q)               //计数输出
);initial beginsys_rst_n = 1'b0;sys_clk = 1'b1;#200;sys_rst_n = 1'b1;repeat(100) begin      cin <= 1'b0;              //输出4个周期低电平#(T * 4);   cin <= 1'b1;              //输出1个周期高电平#(T);        endcin <= 1'b0;#(200 * T);$stop;
endalways #(T/2) sys_clk = ~sys_clk;endmodule

测试文件产生一个时钟信号 sys_clk 和一个计数使能信号 cin,cin信号为4个时钟的低电平,然后1个时钟的高电平.也是说没5个时钟周期计数器就会计数一次。

下来仿真一下,看看输出波形。

放大波形看看cin和计数值关系

可以看到每个cin信号为高时,计数值加1,当计数值为9时,输出cout信号输出一个高脉冲。

再放大波形看看cout和cin的输出时序

可以看到当计数值为9时,cin信号出现高电平,cout延时一个时钟周期才输出的一个高电平。按照正常计数逻辑来说,当低位9再加1时,低位变为0,同时向高位进1,计数和进位是同时发生的。而这个却出现了计数和进位不同步的情况。出现这种情况是为什么呢?那就要分析分析代码中的进位信号。

always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cout <= 1'b0;else if(cnt == 4'd9 && cin == 1'b1)cout <= 1'b1;elsecout <= 1'b0;
end

进位的always语句执行块,是在时钟的上升沿或者复位信号的下降沿才会进入。

在波形中可以看出,在蓝色光标处,时钟的上升沿cin信号到来,计数值加1,此时计数值为9,同时cin信号为1,此时cout信号应该要输出高电平了,但是由于cout是由时序逻辑控制的,只有在时钟的上升边沿always语句才会执行,所以只有等到下一个时钟上升沿cout才有机会输出高电平。

说明cout信号通过时序逻辑来实现的话,会有延时,不符合设计的实时变化要求,那么就把cout的实现改成用组合逻辑来实现,看看是什么效果。

在代码中将cout改为组合逻辑

/*
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)cout <= 1'b0;else if(cnt == 4'd9 && cin == 1'b1)cout <= 1'b1;elsecout <= 1'b0;
end
*/
将时序逻辑改为组合逻辑assign  cout = (cnt == 4'd9 && cin == 1'b1) ? 1'b1 : 1'b0;  

将cout改为组合逻辑实现,当计数值cnt为9,同时cin为高电平时,cout值为1,否则cout值为0。

这样当cin和cnt的值由任何变化时,cout值也跟着会变化,不受到时钟上升沿的影响。

注意将cout由时序逻辑改为组合逻辑时,要在初始化中将cout由寄存器类型改为线网类型。

重新编译代码后,查看波形。

这时可以看到cout信号和cin信号会同时变为高电平,不会有一个时钟周期的延迟。

当计数值变为9时,cin由低电平变为高电平继续计数时,计数值清0,同时进位输出也变为高电平。符合设计的要求。

通过上面的例子可以看到,当输出信号需要实时跟随输入信号变化时,就必须用组合逻辑来实现。其余情况下用时序逻辑来实现。

FPGA学习笔记---时序逻辑与组合逻辑分析比较相关推荐

  1. FPGA学习笔记(1)简单的时序逻辑电路——流水灯

    FPGA学习笔记(1)简单的时序逻辑电路--流水灯 编程语言为Verilog HDL 原理 (1)设计一个计数器,使开发板上的4个LED状态每500ms翻转一次.开发板上的晶振输出时钟频率为50MHz ...

  2. 小梅哥FPGA学习笔记

    小梅哥FPGA学习笔记 一.38译码器 功能: 译码器其任一时刻的稳态输出,仅仅与该时刻的输入变量的取值有关,它是一种多输入多输出的组合逻辑电路,负责将二进制代码翻译为特定的对象(如逻辑电平等).38 ...

  3. FPGA学习笔记(五)Testbench(测试平台)文件编写进行Modelsim仿真

    系列文章目录 一.FPGA学习笔记(一)入门背景.软件及时钟约束 二.FPGA学习笔记(二)Verilog语法初步学习(语法篇1) 三.FPGA学习笔记(三) 流水灯入门FPGA设计流程 四.FPGA ...

  4. 吴恩达《机器学习》学习笔记七——逻辑回归(二分类)代码

    吴恩达<机器学习>学习笔记七--逻辑回归(二分类)代码 一.无正则项的逻辑回归 1.问题描述 2.导入模块 3.准备数据 4.假设函数 5.代价函数 6.梯度下降 7.拟合参数 8.用训练 ...

  5. FPGA学习笔记(八)同步/异步信号的打拍分析处理及亚稳态分析

    系列文章目录 一.FPGA学习笔记(一)入门背景.软件及时钟约束 二.FPGA学习笔记(二)Verilog语法初步学习(语法篇1) 三.FPGA学习笔记(三) 流水灯入门FPGA设计流程 四.FPGA ...

  6. FPGA学习笔记——分频电路设计

    FPGA学习笔记--分频电路设计 发布时间:2015-10-3023:29:52 分频就是用一个时钟信号通过一定的电路结构变成不同频率的时钟信号,这里介绍一下整数分频电路的设计方法.整数分频电路有偶数 ...

  7. FPGA学习笔记_UART串口协议_串口接收端设计

    FPGA学习笔记 1. UART串口协议以及串口接收端设计 1 原理图 2 Verilog 代码 3 Modelsim仿真 4. FPGA板级验证 1.1 串口协议接收端设计 目标:FPGA接收其他设 ...

  8. FPGA学习笔记之Altera FPGA使用JIC文件配置固化教程

    FPGA学习笔记之Altera FPGA使用JIC文件配置固化教程 很多做过单片机的朋友都知 道,我们在对MCU烧写完程序固件后,那么该程序固件就存储在了该MCU内部.即使MCU断电了再重新上电,程序 ...

  9. 吴恩达《机器学习》学习笔记五——逻辑回归

    吴恩达<机器学习>学习笔记五--逻辑回归 一. 分类(classification) 1.定义 2.阈值 二. 逻辑(logistic)回归假设函数 1.假设的表达式 2.假设表达式的意义 ...

最新文章

  1. google的阴阳历转换查询
  2. 【数据挖掘】卷积神经网络 ( 视觉原理 | CNN 模仿视觉 | 卷积神经网络简介 | 卷积神经网络组成 | 整体工作流程 | 卷积计算图示 | 卷积计算简介 | 卷积计算示例 | 卷积计算参数 )
  3. 我与前端 | 因兴趣起源
  4. OpenGL在frag着色器中模拟点光源
  5. 七种布局显示方式效果及实现
  6. linux我如何查看一个脚本的路径,linux获取shell脚本所在绝对路径操作介绍
  7. python语言的三个主要特点_python干货|新总结的4个python语言的特点,这几个细节值得关注...
  8. 简单的java日志记,Java 记要 日志,log
  9. as, idea 出现 Gradle's dependency cache may be corrupt 错误分析
  10. (jdbc和cmd)sqlite数据迁入mysql(导入导出)
  11. 电脑可以登微信但是登不上网页
  12. ubuntu 强制删除文件夹
  13. 眼睛到底是冷敷好还是热敷好?敷眼睛是个技术活!
  14. 转变磁盘格式(转成gpt)
  15. 《异常点检测》 - 第十章阅读记录 - 离散序列的异常点检测
  16. 学习笔记:星火第一讲-使用Apollo 学习自动驾驶
  17. linux proftpd 关闭匿名用户,Linux ProFTPd安装与卸载详细介绍_Linux_脚本之家
  18. 编写函数,对传送过来的三个数选出最大值和最小值,并通过形参传回调用函数
  19. 7-14 凯撒密码 (10 分)
  20. HJ87 密码强度等级(一把过)

热门文章

  1. 原来,我们的时间这样被科技巨头们瓜分(转)
  2. Solr4.3整合到Tomcat中并添加MMSeg4j中文分词器
  3. 做企业:什么都可以外包么
  4. Linux下 对文件行数打乱(乱序排列)
  5. 删除ELK中的数据。。
  6. 递归和对面对象编程初步
  7. HDU 3586 Information Disturbing (树形DP,二分)
  8. ecshop 全目录说明
  9. python词组语义相似度_文本匹配,语义相似度,匹配相似短语/单词python语义wordNet模糊匹配...
  10. android 判断fragment类型,Android 判断当前Fragment是否可见(Visible)