简单组合逻辑-仿真文件的编写与巩固

步骤与实现

  • 简单组合逻辑-仿真文件的编写与巩固
    • 一、二选一选择器
    • 二、3-8译码器
    • 三、半加器
    • 四、层次化设计(全加器的实现)

一、二选一选择器

1.verilog代码写完之后,进行语法错误检查和全编译,编译成功之后,需要进行仿真文件的编写,在已有的模板上进行修改。(接上一篇如何获取仿真文件模板)
2.修改主要是进行输入信号的赋值,仿真文件内容组成如下:
(以按键点亮自己的led灯仿真文件代码编译为例看内容组成)

3.二选一选择器就是,三个输入,一个输出,当选通信号为低电平,选择输入2=输出,当选通信号为高电平,选择输入1=输出。二选一选择器verilog代码如下:

module choose
(input wire [0:0] in_1,input wire    in_2,input wire   sel,output reg out
);
always@(*) //*=in_1,in_2,sel信号if (sel == 1'b1)out = in_1 ;//因为是组合逻辑,所以使用阻塞赋值,顺序进行   elseout = in_2;
endmodule

新手编辑,output reg 忘记加输出信号out报错,该软件里面的报错提醒没有那么详细,自己敲代码的时候一定要注意仔细!
4.仿真文件代码(含详细注释)

`timescale 1 ps/ 1 ps  //关键词时间尺度,时间单位 1ps
module choose_vlg_tst();   //仿真文件名称 仿真文件:对被仿真的模块进行一个输入信号的模拟//reg eachvec;  //类似于时钟信号,有clk信号不用它each,没有clk信号时用它reg [0:0] in_1;
reg in_2;
reg sel;wire out;  //对输入信号、输出信号进行定义//接下来是实例化,我们需要在仿真文件中调用被仿真的文件
//我们将仿真模块中生成的in_1与我们被仿真模块in_1相连接,实现了模块的实例化
choose i1 (.in_1(in_1),.in_2(in_2),.out(out),.sel(sel)
);
initial     //上电只执行一次,在仿真文件之中,begin end之间的语句顺序执行,在没有延时的情况下,看起来就像是并列运行,begin end就相当于括号的作用,在多行赋值时必须用到
//initial语句主要是为了给输入信号进行一个初值的赋值,使用非阻塞赋值,并列运行
//在模板中主要需要填写initial语句,给输入赋值begin                                                  in_1 <= 1'b0;in_2 <= 1'b0;sel <= 1'b0;  //初始输入信号均赋值为低电平0$display("Running testbench");                       end      always #10 in_1  <= {$random}%2;
always #10 in_2  <= {$random}%2;
always #10 sel  <= {$random}%2;// 对这三个输入信号进行随机数赋值,用always语句   #+时间单位,#号表示时间延时,延时多少个时间单位,使用阻塞赋值 ,系统函数:随机数的产生 $random, 求模运算符%
//将产生的随机数对2进行求模, 产生的值只有0和1,即高电平和低电平的变化
//每隔10ps对in_1进行一次赋值,赋值是一个随机数,有可能是0,有可能是1                                                    initialbegin $timeformat(-12,0,"ps",6);  //设置时间格式的系统函数//为了便于观察,写入监测的系统函数$monitor("@time %t:in_1=%b in_2=%b sel=%b out=%b",$time,in_1,in_2,sel,out);  //时间:各个信号的电平变化,即对各个信号的电平变化进行实时打印,方便观察end//@eachvec;                                              endmodule

过程中有几个问题:
①对于代码中eachvec问题的解决,这里我是都屏蔽了,不屏蔽会报错,我也不知道怎么解决,屏蔽后能正确得到波形。
问题解释参见
链接: 【原创】菜鸟学习Modelsim 之 遇到的问题及解决方法.

看5和6的解答
②系统函数$timeformat
③系统函数display 用于信息的显示与输出
④为什么报错?因为英文字符和中文字符的差别,这种低级错误!

5.RTLSimulation 仿真波形(看sel选通信号的跳变)

二、3-8译码器

1.在写verilog代码之前:
①先明确译码器含义
编码的逆过程,把代码状态的特定含义翻译出来的过程叫译码译码器是可以将输入二进制代码的状态翻译成输出信号,以表示其原来含义的电路。
译码器是一种多输入多输出组合逻辑电路器件, 其可以分为变量译码和显示译码两类。这里演示3-8译码器。
②明确功能
3-8译码器,3个输入,8位输出,其输入输出真值表如下:

2.编写verilog代码,与选择器类似
这里有两种编写方式,if-else分支语句和case分支语句
注意点:①8路输出,位宽8比特

②编码过程中避免产生latch,要完整描述编码过程。
if-else多一条else语句,可以任意赋值;
case中default语句,同样可以任意赋值。
参考链接: FPGA设计中latch的产生原因、危害与避免措施.

具体编码实现如下:

//组合逻辑实现3-8译码器
module decoder
(input wire [0:0] in_1,input wire    in_2,input wire   in_3,output reg [7:0] out
);
/*always@(*) //*=in_1,in_2,in_3信号if ({in_1,in_2,in_3} == 3'b000)out = 8'b0000_0001 ;//因为是组合逻辑,所以使用阻塞赋值,顺序进行else if({in_1,in_2,in_3} == 3'b001)  out = 8'b0000_0010 ;else if({in_1,in_2,in_3} == 3'b010)    out = 8'b0000_0100 ;else if({in_1,in_2,in_3} == 3'b011)    out = 8'b0000_1000 ;else if({in_1,in_2,in_3} == 3'b100)    out = 8'b0001_0000 ;else if({in_1,in_2,in_3} == 3'b101)    out = 8'b0010_0000 ;else if({in_1,in_2,in_3} == 3'b110)    out = 8'b0100_0000 ;else if({in_1,in_2,in_3} == 3'b111)    out = 8'b1000_0000 ;      elseout = 8'b0000_0001 ; //当上述调节都不满足的时候,输出这条语句,当然也可以输出别的,可以进行任意赋值,这个else语句主要是避免产生latch//产生latch的解释:https://blog.csdn.net/perfect_lun/article/details/51818886*///另一种分支语句的表示
always@(*)case({in_1,in_2,in_3})3'b000:out = 8'b0000_0001;3'b001:out = 8'b0000_0010;3'b010:out = 8'b0000_0100;3'b011:out = 8'b0000_1000;3'b100:out = 8'b0001_0000;3'b101:out = 8'b0010_0000; 3'b110:out = 8'b0100_0000;3'b111:out = 8'b1000_0000;default:out = 8'b0000_0001;endcase endmodule

编写代码编译完成之后,查看并比较其RTL图。


可以发现case语句是比if-else语句简洁的多,这主要是因为在if语句中的分支是有优先顺序的,在if-else语句中存在优先级,先判断第一个优先条件,看条件是否满足,逐步执行;而case语句在任何时候都不存在优先级的问题,只要这个条件与下列条件其中一个吻合,则执行。

另外,注意编译中可能会出现3个严重警告:

因为当前不做时序分析,故不需要进行.sdc文件的编写(目前我对这一文件的编写还不清楚)

3.仿真文件的编写(含详细注释)

`timescale 1 ps/ 1 ps //时间尺度
module decoder_vlg_tst();  //模块名//reg eachvec;reg [0:0] in_1;
reg in_2;
reg in_3;  //输入信号定义wire [7:0]  out;  //输出信号定义decoder i1 (.in_1(in_1),.in_2(in_2),.in_3(in_3),.out(out)
);  //模块实例化
initial   //输入信号的初始化,使用initial语句,全部初始化为低电平                                           begin                                                  in_1 <= 1'b0;in_2 <= 1'b0;in_3 <= 1'b0;$display("Running testbench");
end                                                    always #10 in_1  <= {$random}%2;
always #10 in_2  <= {$random}%2;
always #10 in_3  <= {$random}%2;   //因为输入信号是模拟输入,always语句进行一个随机数的赋值,不是0就是1                                          //@eachvec;                                              //为了方便模块仿真和便于观察,添加几个系统函数
initialbegin $timeformat(-12,0,"ps",6);  //设置时间格式//为了便于观察,写入监测的系统函数$monitor("@time %t:in_1=%b in_2=%b in_3=%b out=%b",$time,in_1,in_2,in_3,out);  //时间:各个信号的电平变化,即对各个信号的电平变化进行实时打印,方便观察end                                                    endmodule//时模输入输出实例化,initial初始always赋值加系统(时间格式和监测)

同样屏蔽了eachvec(对这个信号弄得还不是很清楚)

将仿真文件的内容和编写总结成:
时模输入输出实例化,initial初始always赋值加系统(时间格式和监测)

4.RTL仿真
包含一些使用技巧:

各个输入信号的初始赋值和随机数赋值都是随机的,可以看到输出能够很好的实现译码功能。

三、半加器

1.在写verilog代码之前:
①先明确半加器含义
半加器——全加器——层次化设计方法
加法器主要用于两个数或者多个数相加,加法器分为半加器和全加器。
半加器是指对两个输入数据位相加,输出一个结果位和进位,没有进位输入的加法器电路,实现两个一位二进制数的加法运算电路。
全加器除了加数和被加数加和外,还要加上上一级传进来的进位信号。

②明确功能
半加器,2个输入,2个输出(一个结果,一个进位),其输入输出真值表如下:

2.编写verilog代码
这里有两种编写方式,分别用always@(*)和assign函数写。

module half_adder
(   input wire  [0:0] in_1,input wire   in_2,output wire  sum,output wire  count
);assign {count,sum} = in_1 + in_2;/*always@(*)begin{count,sum} <= in_1 + in_2;end
//用assign 输入信号类型就是wire,用always 输入信号类型就是reg!!易错
*/
endmodule

always@(*)和assign函数的区别:
参考1: verilog基本语法之always和assign.
参考2: Verilog中 reg和wire 用法和区别以及always和assign的区别.

编译后RTL图可以看到已经被综合成了一个加法器。

3.仿真文件编写(与前两个类似)

时模输入输出实例化,initial初始always赋值加系统(时间格式和监测)

代码如下:

`timescale 1 ps/ 1 ps  //时间
module half_adder_vlg_tst();  //模块名//reg eachvec;reg [0:0] in_1;
reg in_2;wire count;
wire sum;    //输入输出定义half_adder i1 (.count(count),.in_1(in_1),.in_2(in_2),.sum(sum)
);    //实例化initial                                                begin                                                  in_1 <= 1'b0;in_2 <= 1'b0;$display("Running testbench");                       end    //initial初始                                                always #10 in_1  <= {$random}%2;
always #10 in_2  <= {$random}%2;  //always赋值initial   //initial系统函数格式+监测begin $timeformat(-12,0,"ps",6);  $monitor("@time %t:in_1=%b in_2=%b sum=%b count=%b",$time,in_1,in_2,sum,count);  end    //@eachvec;                                              endmodule

4.RTLsimulation结果

四、层次化设计(全加器的实现)

1.在写verilog代码之前:
①先明确层次化设计含义
数字电路中根据模块层次不同有两种基本的结构设计方法:自底向上和自顶向下的设计方法。

自底向上:基本单元——高级单元——系统
自顶向下:系统——高级单元——基本单元——子模块——EDA元件库中的元件

层次化设计思想对这两种设计方法是混合使用的,这里主要是利用层次化设计思想基于第三部分的半加器去实现全加器。
②明确功能,全加器
3路输入,2路输出,输入为:两个数+上一级的进位信号
这里采用层次化设计思想去设计。(将一个全加器分成两个半加器)
明确:一个全加器并不是最基本的结构,可以由两个半加器构成。
结构框图如图:

2.verilog代码编写
注意点:
①需要对两个半加器进行实例化;因为两个半加器就是本身的子模块,类似于两个半加器芯片构成一个全加器功能;
②实例化后的half_adder.v文件还要加载到files中,在创建工程project的时候就添加进去,否则会报错!

具体代码实现如下:

module full_adder
(   input wire  [0:0] in_1,input wire   in_2,input wire   cin,output wire  sum,    //求和信号是由第二个半加器直接输出,故用wire型output wire  count
);//端口列表编写完成wire h0_sum ;
wire h0_count ;
wire h1_count ;//既然是用半加器实现全加器,就一定要对半加器实例化,就是接口对接口要有个协议对准half_adder i1 (.count(h0_count),.in_1(in_1),.in_2(in_2),.sum(h0_sum)
);    half_adder i2 (.count(h1_count),.in_1(h0_sum),.in_2(cin),.sum(sum)
); //两次实例化,实例化名称不能相同//类似于使用两个半加器芯片实现一个全加器//用assign语句对两个进位信号进行或运算,把最终的结构作为进位信号输出
assign count = (h0_count | h1_count);endmodule

编译之后,其RTL视图如下:

3.仿真文件的编写

时模输入输出实例化,initial初始always赋值加系统(时间格式和监测)


`timescale 1 ps/ 1 ps  //时间
module full_adder_vlg_tst();  //模块名reg eachvec;reg cin;
reg [0:0] in_1;
reg in_2;wire count;
wire sum;  //输入输出定义full_adder i1 (.cin(cin),.count(count),.in_1(in_1),.in_2(in_2),.sum(sum)
);  //实例化initial                                                begin                                                  in_1 <= 1'b0;in_2 <= 1'b0;cin <= 1'b0;$display("Running testbench");                       end     //initial初始 always #10 cin   <= {$random}%2;
always #10 in_1  <= {$random}%2;
always #10 in_2  <= {$random}%2;  //always赋值                                             initial   //initial系统函数格式+监测begin $timeformat(-12,0,"ps",6);  $monitor("@time %t:in_1=%b in_2=%b cin=%b sum=%b count=%b",$time,in_1,in_2,cin,sum,count);  end                                                      endmodule

需要注意,这里要进行仿真时间的限制,否则在生成波形界面全加器会一直仿真下去,直到按stop停止,设置仿真时间为1us。

4.RTL Simulation仿真结果
①在sim中查看,最上层为顶层仿真模块,打开为内部两个实例化的半加器模块;

②默认只添加仿真模块的波形,回到sim界面,添加顶层模块的波形和实例化半加器的波形;

③在wave界面使用快捷键CTRL+A 全选,CTRL+G对波形进行分组,用restart对波形进行重新生成(该图还未重新生成,未设置仿真时间为1us,仿真一直在进行,数据有缺失);

④点击全局视图,添加参考线对波形进行放大,验证波形。(设置end simulation at 1us 之后)

管脚绑定——程序固化——上板验证

quartus仿真文件的编写相关推荐

  1. verilog中tb仿真文件模板

    以前总是用ise,它会自动编写仿真文件,所以用了vivado以后就有点生疏.留一个备份,万一以后用到了方便找. 1. Testbench的概念 ​        Testbench 是一种用任意语言编 ...

  2. quartus仿真系列0:基于原理图仿真的基本操作

    翻一本数电书时,无意发现利用maxplus2原理图来仿真设计,唤起了在上数电实验课时的一个设想,利用quartus仿真时序电路的波形图,这样更加直观清晰(不用我手画了).我们数电课上只教了利用VHDL ...

  3. 【LabVIEW FPGA图形化】 ngc、edf网表文件的编写:LED流水灯

    [LabVIEW FPGA图形化] ngc.edf网表文件的编写:LED流水灯 文章目录 前言 一.什么是FPGA图形化设计 二.为什么要学习FPGA图形化开发? 三.Xilinx Spartan-6 ...

  4. 电力系统励磁涌流有关的问题分析。 可以通过MATLAB中m文件便编写产生励磁涌流

    电力系统励磁涌流有关的问题分析. 可以通过MATLAB中m文件便编写产生励磁涌流,也可以通过simulink仿真出励磁涌流. 可以仿真分析影响励磁涌流的因素,以及抑制励磁涌流的措施. 可以研究识别励磁 ...

  5. 西电A测 | 基于Arduino uno的电子密码锁仿真系统 | 使用Proteus仿真 | Arduino IDE编写

    西电A测 | 基于Arduino uno的电子密码锁仿真系统 | 使用Proteus仿真 | Arduino IDE编写 | ATMEGA328P 电子密码锁系统 Arduino UNO Arduin ...

  6. itmz文件如何打开_如何使用proteus8打开低版本proteus7的仿真文件?

    我们在使用proteus进行仿真的时候会遇到版本的问题,proteus8打开低版本proteus7的仿真文件就是其中之一,本文就为大家介绍如何使用proteus8打开低版本proteus7的仿真文件. ...

  7. Cacti 插件中setup.php 文件的编写

    Cacti 插件中setup.php 文件的编写 名词: 初始化函数   预定义函数  cacti 插件存放在 /plugins 目录,由setup.php与cacti 做关联调用; setup.ph ...

  8. 02_JNI中Java代码调用C代码,Android中使用log库打印日志,javah命令的使用,Android.mk文件的编写,交叉编译

     1  编写以下案例(下面的三个按钮都调用了底层的C语言): 项目案例的代码结构如下: 2 编写DataProvider的代码: package com.example.ndkpassdata; ...

  9. 手把手教你pyqt中.qrc图片文件的编写和使用

    pyqt.qrc图片文件的编写和使用 pyqt图片资源文件的引用 0x00说明: 可以通过qrc文件将我们要用的图片转化成py代码, 然后引入到需要用到的地方. 这将有利于将程序打包成exe后图片的正 ...

  10. Linux平台Makefile文件的编写基础篇和GCC参数详解

    问:gcc中的-I.是什么意思....看到了有的是gcc -I. -I/usr/xxxxx..那个-I.是什么意思呢 最佳答案 答:-Ixxx 的意思是除了默认的头文件搜索路径(比如/usr/incl ...

最新文章

  1. 「NLP」ALBERT:更轻更快的NLP预训练模型
  2. (001) RN开发之Mac搭建开发环境
  3. Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅
  4. java 时间戳验证_Java中带有时间戳的数字签名
  5. 五个举措:现代化Jenkins 和终结“Jenkinsteins”
  6. 浅析Java中对象的创建与对象的数据类型转换
  7. dpkg status database is locked by another process
  8. 191202-GETJOB-捡历的写法
  9. Sql Server和oracle的函数区别总结
  10. 杭电1713相遇周期
  11. python hello world
  12. Python画四张子图-导入数据
  13. How to install php evn on ubuntu
  14. 如何打造自己的PoC框架-Pocsuite3-框架篇
  15. Android P update_engine分析(三) --boot_control的操作
  16. 按理说机械硬盘和固态硬盘使用得当寿命几乎一样长,但为什么网上传言机械硬盘更容易坏?
  17. 什么是Oauth2.0授权,四种授权模式
  18. c 语言printf输出,位数不够补0
  19. 看完这篇文章再也不用担心代码格式不对无法合并了
  20. board (双联通分量)

热门文章

  1. 固高运动控制卡教程_基于固高运动控制卡的关节阻抗控制设计
  2. Python:笨方法学 Python3-课程 41 笔记
  3. SAI的操作与快捷键
  4. 全志 Allwinner V3S 开发环境搭建 (二)安装必要工具
  5. Android_GNSS原始数据转为Rinex文件
  6. python与vb语言_vb.net和python区别是什么
  7. pitch yaw roll 角度分别是什么
  8. 利用Think远程代码执行漏洞进行脱库上传免杀木马情报
  9. 基于JavaEE的班级管理系统设计与实现_信息管理__JSP网站设计_SQLServer数据库设计
  10. access h3c交换机光口_华为交换机的配置及:access、trunk、hybird端口详解