FPGA编程入门(半加器,1位全加器和4位全加器的实现)

目录



一、Verilog编程入门

1.1 门电路入门练习

(1)与门

本题要求使用 Verilog 语言描述一个模块,实现与门的作用。

从第 4 题开始,是用 Verilog 描述各种 “门”,这也就是 Verilog 硬件描述语言中,描述二次的由来。描述就是我们用 Verilog 的语法,通过写下几句代码来实现一个电路。从最简单的门到 CPU 都可以使用 HDL 描述。

题目给出的模块如下图,有三个 wire : a,b 以及 out。a,b 信号已经由模块的输入端口驱动,但图中黑色的部分中,wire out 还没有被任何信号驱动。本题要写一个 assign 语句,使 a,b 信号经过与门的输出驱动 wire out 信号。

显然,assign 语句的实现和前一题非常接近,只是增加了一个输入信号。和前一题不同的是,我们在这里强调了信号是被驱动(drive)的,被驱动的含义可以理解为,该信号的取值取决于另一个连接到它的信号的值,该信号的值随着另一个信号的值改变而改变。下图中模块的输入端口 input wire 被外部连接到模块的信号所驱动。assign 语句映射到具体的硬件上,就是产生了信号的驱动,由右值驱动左值。

说道 assign,一个 wire 信号不能被多个信号同时驱动(当一个信号说往东,另一个信号说往西,两个信号还要同时驱动我时,我到底该往哪?另一个方面,一个没有驱动者(driver)的信号的值会处于未定义的状态,好在综合器一般会免费给他安排一个,将其信号值驱动为 0.

在这里插入图片描述

代码:

module top_module( 
    input a, 
    input b, 
    output out );
   	assign out=a&b;

endmodule

结果:
在这里插入图片描述

(2)或非门

本题要求使用 Verilog 实现一个 NOR 门,注意这里其实是或非门,而不是更常见的异或门,或非门是或门的输出取反。

assign 语句将某个值赋予 wire 信号,这个 value 可以是常量,也可以是一个复杂的逻辑表达式,综合器会综合出相应的逻辑门实现。assign 语句代表的始终是连续赋值,因为当输入信号改变时,输出信号会重新“计算”。和一个逻辑门的工作方式相同,输入改变,输出对应改变。

在这里插入图片描述

代码:

module top_module( 
    input a, 
    input b, 
    output out );
    assign out=~(a|b);
endmodule

结果:

在这里插入图片描述

(3)同或门

XNor 的中文是什么,其实应该是同或门。

我们首先复习下数电,同或门 (XNor Gate) 是异或门 (Nor Gate) 的取反输出。异或门的输入输出可以概括为:(输入)相同(输出)为 0 ,不同为 1 。

在这里插入图片描述

代码:

module top_module( 
    input a, 
    input b, 
    output out );
    assign out = ~(a^b);
endmodule

结果:

在这里插入图片描述

1.2 组合电路入门练习

(1)Declaring wires

之前电路足够简单,我们能直接表示出输入输出信号的逻辑关系,但如果电路变得复杂,那么我们就需要一些中间信号来帮助我们简化描述电路的难度。

定义中间信号的语法格式为

wire foo ;

信号定义语句需要放置于模块的 body 中,就好比 C 语言中,你的中间临时变量需要定义在 main 函数函数体中。模块的 body 指的就是 module 和 endmodule 中间的部分。

这里建议先定义信号,再使用信号,就像 C 语言中一样。原则上,你可以在任何位置定义你的信号,使用前使用后都可以,正如之前的课程中说的那样,语句的顺序对于 Verilog 来说没有关系。但有些仿真工具需要你在使用信号之前定义信号,So,你就这么来吧。

实现下图中的模块。首先创建两个中间信号将与门和或门连接起来,信号的名字随你的便,但好的名字往往影响一个信号的一生,若干年后,你还能依稀记起当年定义这个信号的峥嵘岁月。

注意,与门的输出信号也就是或门的输入信号,所以你不需要再定义或门的输入信号。再提醒一下,信号只能被一个信号驱动,但能驱动多个信号。

按照下图中的逻辑关系,你的代码应该有 4 个 assign 语句,对应四个逻辑门,或者说模块。

在这里插入图片描述

代码:

`default_nettype none
module top_module(
    input a,
    input b,
    input c,
    input d,
    output out,
    output out_n   ); 
	wire	and_1 = a & b;
    wire	and_2 = c & d;
    wire	or_1  = and_1 | and_2;
    assign	out   = or_1;
    assign	out_n = ~or_1;
endmodule

结果:
在这里插入图片描述

(2)7458芯片

本题要实现个稍稍复杂的电路:数电芯片 7458 。它有 10 个输入信号,2 个输出信号。你可以选择对每个输出信号,使用一个 assign 语句,也可以先产生第一级逻辑门输出的 4 个中间信号。有时间的话,两种方式都可以尝试下。
在这里插入图片描述

代码:

module top_module ( 
    input p1a, p1b, p1c, p1d, p1e, p1f,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );
    assign p1y = (p1a & p1b & p1c) | (p1d & p1e & p1f);
    assign p2y = (p2a & p2b) | (p2d & p2c);
endmodule

结果:
在这里插入图片描述

(3)7420 chip

7420 chip是拥有两组4输入的与非门芯片,本练习需要构造一个与7420 chip功能一样的电路,拥有8个输入与2个输出。
在这里插入图片描述

代码:

module top_module
    (
        input  p1a, p1b, p1c, p1d,
        output p1y,
        input  p2a, p2b, p2c, p2d,
        output p2y
    );

    assign p1y = ~(p1a & p1b & p1c & p1d);
    assign p2y = ~(p2a & p2b & p2c & p2d);

endmodule

结果:
在这里插入图片描述

1.3 时序电路入门练习

(1)D flip-flop (Dff)

接下来的题目是属于触发器,锁存器的专题。我们会从用 Verilog 实现基础 D 触发器开始,学习触发器这一数字电路中最重要的电路之一。
在这里插入图片描述

D 触发器是一个电路,存储 1bit 数据,并定期地根据触发器的输入(d)更新这 1 bit 数据,更新通常发生在时钟上升沿(clk)。存储的数据会通过输出管脚(q)输出。

t1时刻: d -> 0

t2时刻: clk->1 上升沿到来,触发器存储的数据变成 0,输出 q 保持为存储的值:0,直到下一个时钟上升沿到来。

t3时刻: d -> 1(d:我变了),q 仍保持 0 不动摇(时钟沿还没来呢)

t4时刻: clk->1 上升沿到来,q->1(q:时钟沿来了,我该变身了)

绝大多数时候,我们不会在 Verilog 代码中显示例化一个触发器(作者没这么做过,但应该是可以做的),我们在时钟敏感的 always 块中的语句一般都会被综合工具转换为相应的触发器。

D 触发器可以认为是一个触发器和一段最简单的组合逻辑块(blob :想表达逻辑块的时候用我,别用 block)的组合。其中组合逻辑块仅仅是一段 wire。(q 直接输出了触发器的存储值)

代码:

module top_module (
    input clk,    // Clocks are used in sequential circuits
    input d,
    output reg q );//

    // Use a clocked always block
    //   copy d to q at every positive edge of clk
    //   Clocked always blocks should use non-blocking assignments
    always@(posedge clk) begin
        q <= d;
    end
endmodule

结果:
在这里插入图片描述

在每个时钟上升沿,输出 q 的值变为输入 d 的值。我们在 always 块中的语句就会被综合工具转换为触发器。这里使用 clk,q,d 对应于触发器的三个端口,反应了转换的对应关系。

(2)D flip-flops (Dff8)

实现 8 个 D 触发器,听上去好像很累的样子,实则 Verilog 语言的抽象帮助我们省去不少麻烦。输入 a,b 的位宽变为 8 位,但 always 块中的语句与上一题完全相同。

综合工具根据位宽,综合出了 8 个 D 触发器。一般没有八位触发器的说法。这里反映了综合工具能分析代码,生成相应的触发器电路,其实综合器还能将复杂得多的语句转为相应的电路。
代码:

module top_module (
    input clk,
    input [7:0] d,
    output [7:0] q
);
    always@(posedge clk) begin
    	q <= d;
    end
endmodule

结果:
在这里插入图片描述

(3)DFF with reset (Dff8r)

在上题的 8 个 D 触发器基础上,这题我们要给触发器配上同步复位端口。

什么同步复位?当时钟上升沿到来时,如果同步复位端有效(本题中复位高电平有效,即 reset),那么任凭你触发器此前输出或者输入的是 0,是 1,输出一律变为 0。

复位电路对于那些经常需要恢复到初始状态的电路是必要的,复位相较于断电重新加载程序恢复到初始状态的速度要快得多。但也有一些电路则不需要复位设计。(作者也是有所耳闻那些不需要复位的电路,平常自己还是会加上复位电路)

代码:

module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);
always@(posedge clk) begin
q <= (~{8{reset}} & d);
end
endmodule

从语法的角度来说,reset 实现就是加一个 if 语句判断 reset 是否有效,有效就将输出 q 置为 0 。

但从电路的角度来说,电路的角度往往更加重要,是使用一个带有复位端的 D 触发器 fdr,另一种 D 触发器单元。

结果:
在这里插入图片描述

二、Logisim进行仿真设计

2.1 认识全加器

2.1.1 半加器

半加器是指对输入的两个一位二进制数相加(A与B),输出一个结果位(SUM)和进位(C),没有进位的输入加法器电路,是一个实现一位二进制数的加法电路。

真值表

被加数a加数a和s进位c
0000
0110
1010
1101

逻辑表达式
根据上述的真值表,当A和B相同时SUM为0,否则为1;逻辑关系属于异或;当A和B同时为1时,C等于1,其余都为零,逻辑关系为与。
所以我们可以得到如下的逻辑表达式:
在这里插入图片描述

逻辑电路图
在这里插入图片描述

2.1.2 全加器

全加器是指对输入的两个二进制数相加(A与B)同时会输入一个低位传来的进位(Ci-1),得到和数(SUM)和进位(Ci);一位全加器可以处理低位进位,并输出本位加法进位。多个一位全加器进行级联可以得到多位全加器。常用二进制四位全加器74LS283。

真值表

被加数a加数b低进位cin和s进位cout
00000
00110
01010
10010
11001
01101
10101
11111

逻辑表达式
由全加器的定义理解我们可以知道当Ai和Bi异或后再与Ci-1进行异或得到SUMi,结合真值表,我们可以知道当Ai、Bi、Ci-1只要有两个以上的1是进位Ci就等于1;所以只需要每两变量求与,结果再求或就可以满足要求。由此我们可以得到最常用的逻辑表达式:

在这里插入图片描述

逻辑电路图
根据逻辑表达式绘制两种逻辑电路图如下:

在这里插入图片描述

2.2 Logisim完成一个1位全加器的设计并测试

2.2.1 先设计设计一个1位半加器电路

采用异或门和与门结合设计一个简单的半加器:

在这里插入图片描述

2.2.2 在半加器电路基础上,实现一位全加器电路

在设计全加器时,因为会有三个数进行运算,因此可以简化为先进行两个输入数的运算,再进行其和与进位数的运算,即两次1bit运算。同时,在计算结果时,除了结果数,还要提出进位数供下一位计算。因此,在Logisim中,我们可以采用两个半加器相结合的方式设计一个全加器,参考如下:
在这里插入图片描述

三、基于Quartus进行实验并仿真

3.1 输入原理图实现1位加法器

创建工程项目过程:
启动 Quartus软件,选择File->New Project Wizard
在这里插入图片描述

填写工程名称:
在这里插入图片描述

选择第一个,直接Next
在这里插入图片描述

这个界面不用管,直接next
在这里插入图片描述

根据使用的FPGA,进行选择芯片系列及类型
在这里插入图片描述

直接next
在这里插入图片描述

完成工程创建,点击finish
在这里插入图片描述

3.1.1 半加器原理图输入

(1)绘制过程实现

首先选择File->New,进入后选择Block Diagram/Schematic File
在这里插入图片描述

选择元件
在这里插入图片描述

添加输入输出,完成效果:

在这里插入图片描述

保存文件,并编译
通过tool->Netlist Viewers->RTL Viewer,查看电路图
在这里插入图片描述

(2)仿真实现

创建一个向量波形文件,选择菜单项 File→New->VWF

在这里插入图片描述

出现这个页面后,双击左侧空白处
在这里插入图片描述

出现这个弹窗,点击Node Finder
在这里插入图片描述

然后添加进来,点击ok
在这里插入图片描述

最后出现波形:
在这里插入图片描述

随便编辑一下信号
在这里插入图片描述

保存文件并启动功能仿真,出现错误,别慌,进行仿真配置,选择tool->launch simulation library complier
在这里插入图片描述

在这里插入图片描述

(3)仿真结果

重新测试仿真:

功能仿真结果:
在这里插入图片描述

时序仿真结果:
在这里插入图片描述

通过仿真结果,可以发现得到的结果与真值表中是相吻合的。

3.1.2 全加器原理图输入

利用半加器元件实现全加器

(1)将设计项目设置为可调用的元件

在打开半加器原理图文件half_adder.bdf的情况下,选择菜中File中的Create/Update→CreateSymbolFilesforCurrentFile项,即可将当前文件h_adder.bdf变成一个元件符号存盘,以待在高层次设计中调用

在这里插入图片描述

(2)绘制过程实现

首先选择File->New,进入后选择Block Diagram/Schematic File
在这里插入图片描述

选择元件
在这里插入图片描述

添加输入输出,完成效果
在这里插入图片描述

保存文件,并编译
通过tool->Netlist Viewers->RTL Viewer,查看电路图
在这里插入图片描述

(3)仿真实现

创建一个向量波形文件,选择菜单项 File→New->VWF
在这里插入图片描述

跟半加器同理添加信号,然后编辑信号
在这里插入图片描述

保存文件并启动功能仿真,出现错误,进行仿真配置,选择tool->launch simulation library complier(同理半加器)

(4)仿真测试结果

功能仿真结果
在这里插入图片描述

时序仿真结果
在这里插入图片描述
通过仿真结果,可以发现得到的结果与真值表中是相吻合的。

四、Verilog编程实现1位加法器

创建Verilog文件

在这里插入图片描述

4.1 代码实现

module full_adder(
	//输入信号,ain表示被加数,bin表示加数,cin表示低位向高位的进位
	input ain,bin,cin,
	//输出信号,cout表示向高位的进位,sum表示本位的相加和
	output reg cout,sum

);
reg s1,s2,s3;
always @(ain or bin or cin) begin
	sum=(ain^bin)^cin;//本位和输出表达式
	s1=ain&cin;
	s2=bin&cin;
	s3=ain&bin;
	cout=(s1|s2)|s3;//高位进位输出表达式
end
endmodule

保存并编译文件
通过tool->Netlist Viewers->RTL Viewer,查看电路图
在这里插入图片描述

4.2 仿真实现

创建一个向量波形文件,选择菜单项 File→New->VWF
在这里插入图片描述

同理添加信号并编辑信号
在这里插入图片描述

功能仿真结果
在这里插入图片描述

时序仿真结果
在这里插入图片描述

通过仿真结果,可以发现得到的结果与真值表中是相吻合的。

五、下载测试

5.1 1位全加器测试

(1)点击引脚配置

在这里插入图片描述

(2)配置引脚

使用3个SW作为输入信号,2个LED作为输出信号
在这里插入图片描述

(3)原理图

在这里插入图片描述

(4)代码下载

代码下载到FPGA板子上
在这里插入图片描述

(5)测试结果

a为,b为1,输入的cin为1,则s=1,进位cout=1,其他测试也通过

在这里插入图片描述

六、四位全加器(实验重点)

6.1 四位全加器的原理图设计

同理将一位全加器设置为可调用的元件,然后设计出原理图

(1)原理图

在这里插入图片描述

(2)RTL电路图如下

在这里插入图片描述

(3)仿真测试

设置信号:
在这里插入图片描述

(4)仿真结果

功能仿真结果
在这里插入图片描述

时序仿真结果
在这里插入图片描述

6.2 四位全加器的Verilog编程实现

(1)创建文件:

在这里插入图片描述

(2)代码实现

//数据流描述4位全加器
module four_adder1(
    	input[3:0] a,b,
    	output[3:0] s,
    	output cout,
    	input cin
);

assign{cout,s} = a+b+cin;
endmodule

(3)RTL电路图

编译代码得到的RTL电路图
在这里插入图片描述

(4)上板烧录

引脚选择
在这里插入图片描述

引脚在板子上的位置对应关系:
在这里插入图片描述

实物图:

在这里插入图片描述

(5)测试结果(重点检验)

此次测试的板子LED1电压不稳定,始终保持亮状态,测试中我们忽略

(1)b3为1,其余为0,结果为1 0 0 0 ,SW7为1,LED4亮,无进位,测试成功
在这里插入图片描述

(2)a3和b3为1,其余为0,结果为1 0 0 0 0,SW3和SW7为1,LED0亮,有进位,cout=1,测试成功

在这里插入图片描述

(3)b2为1,其余为0,结果为0 1 0 0,SW6为1,LED3亮,无进位,测试成功
在这里插入图片描述
结果为跟其真值表相吻合。nice!


总结

这次实验内容很多,基本上是全新的知识点(对于大二没有学过模电数电的我),从软件安装到环境配置过程中踩了很多雷,基本上每个坑都花了几十分钟上网寻找解决方案,但是逐渐摸索的感觉真的很有自豪感,尤其是自己懂得半加器、全加器和四位全加器的原理之后,画电路原理图很是得心应手,但是在组件接线的时候一定要细心,稍有不慎就有一个点漏接了(我就是因为线没接上,一直以为板子有问题,调试了半个钟)。同样这次实验也学会了很多,初步了解了Verilog编程的思想,总体来说,遇到困难和bug不要畏惧,学会使用搜索工具,总能在互联网上找到大佬给出的解决方法,在这里非常感谢大佬给出的各种建议和帮助。

参考

详细讲解半加器、全加器、四位全加器,并使用FPGA实现半加器、全加器
利用Logisim设计半加器、全加器及补码电路

FPGA——1位全加器的实现

Verilog基础编程练习

使用 Quartus-ll 两种方法仿真全加器并烧录到 Intel DE2-115 开发板中验证

一位全加器及四位全加器————FPGA

【Verilog设计—仿真实验】四位加法器的实现

HDLBits 中文导学

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菲菲QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值