基于FPGA层次化设计构成全加器

在上一节半加器中,介绍了全加器可看作两个半加器和一个或门组成。
首先半加器是a+b构成了{c,s}。由于全加器多了一个低位的进位,就是将{c,s}再加上ci-1。此时再使用一个半加器将s与ci-1相加,结果的低位就是全加器的低位si。全加器的高位ci来自于两个半加器的高位,如果有一个高位为1,结果就为1。
设计规划
本例中将采用3个按键作为输入in1,in2和进位信号cin,并采用两个led灯作为信号输出count和sum。根据上一节中全加器真值表,可以画出波形图:
编写代码
module full_adder(input wire in1 , input wire in2 , input wire cin , output wire sum , output wire cout ); //wire define wire h0_sum ; wire h0_cout; wire h1_cout; //------------------------half_adder_inst0------------------------ half_adder half_adder_inst0 ( .in1 (in1 ), .in2 (in2 ), .sum (h0_sum ), .cout (h0_cout) ); //------------------------half_adder_inst1------------------------ half_adder half_adder_inst1 ( .in1 (h0_sum ), .in2 (cin ), .sum (sum ), .cout (h1_cout) ); assign cout = h0_cout|h1_cout; endmodule还记得fpga从入门到精通(一)中的移位寄存器的实例化练习吗?以module模块名(输入,输出)开头,以endmodule结尾。
上一节中已经封装好了half_adder函数,内部的逻辑就不用再次写了,只需要将外部端口和full_adder的端口对应好就可以直接调用。这就是verilog的方便之处,否则完成这个程序,需要将半加器的代码写两次。
再上面的全加器结构图中可以看到端口的对应,其中除了全加器的5个端口外,还需要引入三根连线。需要定义wire变量h0_count,h1_count和h0_sum。
下面完成两次实例化,上一节中的半加器有in1,in2,cout,sum四个端口,第一个半加器分别对应着全加器的输入端口in1,in2,h0_count,h0_sum,第二个半加器分别对应着上一个半加器的结果的低位h0_sum和低位进位cin,h1_count1,全加器的结果的低位sum。全加器的结果的高位count是h0_count和h1_count的或。
现在将.v文件综合一定会报错,因为还没有将half_adder模块添加进来。所以调用时找不到半加器的函数。因此需要将half_adder.v复制到文件夹中并添加到files中:
看rtl视图,两个half_adder模块是可以展开的:
编写testbench
`timescale 1ns/1nsmodule tb_full_adder();reg in1;reg in2;reg cin;wire sum ;wire cout; initial begin in1 <= 1'b0; in2 <= 1'b0; cin <= 1'b0; end always #10 in1 <= {$random} % 2; always #10 in2 <= {$random} % 2; always #10 cin <= {$random} % 2; //------------------------------------------------------------ initial begin $timeformat(-9, 0, ns, 6); $monitor(@time %t:in1=%b in2=%b cin=%b sum=%b cout=%b,$time,in1,in2,cin,sum,cout); end //------------------------------------------------------------ //---------------full_adder_inst------------------ full_adder full_adder_inst( .in1 (in1), .in2 (in2), .cin (cin), .sum (sum), .cout (cout) ); endmodule这里testbench依然是先声明变量,其中输入是reg类型,输出是wire类型。初始化令变量都为0,延时10ns给输入随机赋值0或1,然后实例化将tb_full_adder模块和full_adder模块端口进行连接,就可以观察波形。
需要注意的是!!!在本例中引入了打印模块,可以直观看到随机输入对应输出的真值表。
$timeformat(units_number, precision_number, suffix_string, minimum_field_wdith);timefomat的语法:
第一个参数units_number表示打印的时间值的单位,取0 到-15 之间的整数值:0 表示秒,-3 表示毫秒,-6 表示微秒,-9 表示纳秒, -12 表示皮秒, -15 表示飞秒
第二个参数precision_number表示打印时间值时,小数点后保留的位数。
第三个参数suffix_string在时间值后面打印时间单位。其默认值为空字符串,如果用ns作为单位可以打印ns。
第四个参数minfieldwidth 是时间值与单位字符串的最小长度,不足这个长度,则在字符串之前补空格。其默认值为20。
monitor的语法:
$monitor (“format_string”, parameter1, parameter2, … );当在monitor调用时对多个变量进行监控,当monitor监控的变量中任何一个发生变化时,将会打印出当前仿真时刻的值;如果$monitor监控的所有变量在某一时刻均不改变,将不会打印任何信息。本例中会打印出时间和5个端口值。
对比波形
当端口多了之后,看波形相对不直观了,这里可以通过观察我们刚才打印的数据进行判断。通过view-transcript可以观察transcript窗口:
表格的方式相对直观多了,且输出和预期一致。
分配管脚
全编译后分配引脚,这里的按键和led灯管脚我们已经非常熟悉了。在location这一栏分配引脚,不要和fitterlocation弄混了。
现在cin,in1,in2对应s0,s1,s2,cout对应led0,sum对应led1。那么cin,in1,in2按下去表示为0,点亮的led表示对应为输出为低电平。都不按下去时,应该都熄灭,按下去一个时,led0亮,按下两个时,led1亮,按下三个时,两个led同时亮。
全编译后上板验证
一个都不按下时,都不亮
按下一个时,led0亮
按下两个时,led1亮
全部按下时,两个都亮
组合逻辑电路的实例就结束了,后面是简单的时序逻辑电路实例。

深圳锡膏厂家哪家价格好?
对于SMT贴片中模板的检查,检查内容和方法是什么
联想Z5s与荣耀8X哪个性价比最高
小米翻盖折叠屏手机专利曝光可以上下对称折叠
《深圳市城市照明管理办法》立法听证会讨论节能和照明如何平衡
基于FPGA层次化设计构成全加器
简单的免受超电压汽车视频驱动电路保护程序
价值链透视:消费者不利用手机上网的原因
佛蒙特州州长正在通过推广区块链技术来刺激佛蒙特州的经济发展
中国二极管及半导体器件进口数据详细统计分析概述
消弧线圈的工作原理
工业物联网中常见的RS485接口是什么?以太网能否取代RS485?
缓冲放大器的工作原理和优点
MediaTek Genio 130/130A(MT7931/MT7933) 智能家居之Matter应用方案
虹科方案 | 扩展集群(Stretch Cluster)的介绍与实现
MXM 3.1 Type A嵌入式GPU卡
全球性能最强AI芯片:芯片含光800正式商用
远程医疗需解决资源总量不足分布不均衡的难题
金泰克天启G5SSD评测 让硬盘也加入了可变频的行列
在线研讨会 | 利用 NVIDIA Jetson 赋能工业边缘 AI 应用