
电子科技大学
微电子与固体电子学院
吴洪天
2603001018目录
一、摘要 (2)
二、任务要求 (3)
三、总体设计方案 (4)
四、各模块实现过程与仿真结果 (6)
(一)选手编码锁存器模块 (6)
(二)分频器模块 (9)
(三)蜂鸣器模块 (11)
(四)定时器模块 (15)
(五)显示译码模块 (18)
(六)顶层模块 (21)
五、下载和调试 (26)
六、总结 (27)一、摘要
FPGA(Field Programmable Gate Array)是一种可编程逻辑器件,通过它可以实现各种数字逻辑电路。随着微电子技术的发展,FPGA内部集成了越来越多的门电路单元,利用FPGA可实现的系统也越来越复杂。为了实现对FPGA 的编程配置,需要使用硬件描述语言,最常用的硬件描述语言有VHDL和Verilog hdl两种,由于Verilog语法简单,且接近于C语言,使用也相对较灵活,因此这里选择使用Verilog进行编程。
在这里,我们要用FPGA实现一个四个选手的抢答器。
抢答器是一种典型的异步时序逻辑电路,因此整个系统不受同步时钟的控制。
在进行系统设计时,采用自顶而下的设计思路,先根据系统的整体功能构思出顶层模块的功能,再根据具体的功能分成各种子模块进行设计。在用硬件描述语言进行描述时,可以有两种描述方式,一种是行为描述,另一种是结构描述。行为描述方式按照模块要实现功能用描述性语句描述模块输入对模块输出的影响,这种描述方式简单灵活,可以不用过多考虑具体的电路结构,然而,这样也可能存在所描述的电路无法实现的情况,造成综合工具无法综合;结构描述方式类似于传统构建电路的方法,利用各种已经构造好的模块或元件直接相连形成新的模块,这种描述方式需要考虑电路的具体结构,因此设计起来也相对较麻烦,但可综合率较高。在这个系统的设计中,各个子模块采用行为描述方式进行构造,以提高效率,但需要时刻考虑所描述的语句是否可综合,顶层模块采用结构描述语句,由于顶层模块只需简单地将各个子模块连接在一起,无需考虑电路的工作的过程和原理,因此采用结构描述方式也很容易。
按照上面的思路设计好各个子模块和顶层模块之后,需要进行编译、综合和仿真,仿真用于检验设计的电路逻辑是否有误,编译、综合用于生成最终的下载文件。在这个过程中需要不断地检查和修改错误,直到生成正确的可下载文件。最后的步骤就是进行下载和调试,将综合产生的下载文件下载到FPGA中,接着进行硬件调试,在FPGA开发板中运行系统,检查是否有错误,如果出错,要返回去修改程序,直到系统顺利运行为止。
二、任务要求
设计一个抢答器,满足一下功能要求和指标要求:
1、基本功能
(1)编号为1-4的选手按键抢答;
(2)抢中编号锁定显示,其它无效;
(3)主持人按键控制清零和开始;
(4)选手抢中后在规定时间内答题;
(5)具有报警提示功能,分别提示抢答开始,有人抢答,定时时间到。
2、指标要求
(1)显示组数:1-4;
报警延时:学号+100,单位ms;
(2)答题时间:10s。
三、总体方案设计
抢答器是一个典型的异步时序逻辑电路,按照题目要求,有四个选手抢答,主持人控制开始和清零。当主持人按下开始按钮后,选手可以开始抢答,最先按下按钮的选手选中,并在数码管中显示选手编号,同时,定时器开始倒计时,倒计时时间为10秒,由另外一个数码管显示时间。在抢答开始、选手选中和定时时间到时,蜂鸣器都要发出一声报警,报警时长为118毫秒。主持人按下清零按钮后,系统的状态恢复到初始状态,数码管显示清零。
要实现这个抢答器,有三种方案可以选择。方案一是用单片机实现,这种方案硬件电路简单,调试方便,成本也较低;方案二是用中小规模数字集成电路搭建,这种方案电路结构复杂,容易出错,调试也比较麻烦;方案三是用FPGA实现,利用硬件描述语言在FPGA内部形成所需要的硬件逻辑,从而实现所需要的功能,这种方案硬件电路简单,容易修改,调试也很方便。这里采用方案三,硬件描述语言使用Verilog。
按照题目要求,电路实现的原理如图1所示。
图1
主持人控制开关控制电路中所有的时序逻辑模块,提供抢答开始信号和清零信号,优先编码器在接收到抢答开始信号后等待选手抢答,并锁存第一个抢到选手的编码,同时由时序控制电路启动报警电路报警和定时电路倒计时,选手编码送至显示译码,由数码管显示选手编号。定时电路将当前的倒计时时间编码送至显示译码,由数码管显示时间,并在定时时间到时,通过时序控制电路启动报警电路报警。
为了实现抢答器功能,整个系统由六个子模块构成,分别为分频器模块、选手编码锁存器模块、蜂鸣器模块、定时器模块和两个显示译码模块。整个系统的组成框图如图2所示。
图2
四、各模块实现过程与仿真结果
各个模块的功能、实现方法和仿真结果如下。
(一)选手编码锁存器模块
1、模块功能描述
选手编码锁存器用于监测抢答环节,当抢答开始后判断选中选手。
在抢答过程中,选手编码锁存器首先判断抢答是否开始,即主持人开始按键是否按下,当抢答开始后,监测第一个按下按键的选手,并屏蔽后面按下按键的选手。当有选手按下按键时,锁存器锁存第一个按下按键的选手的编号,并输出一个选手抢中信号s。
模块逻辑框图如图3所示。p1~p4分别连接4个选
手抢答按键,start和clr分别连接主持人开始和清零按键,
当抢答开始并有选手按下抢答按键时,q[3]~q0]输出抢中
选手的四位二进制编码,并在s输出端输出一个选手抢中
信号。
2、模块实现过程
为了实现主持人开始按钮对选手抢答的控制作用和第图3
一个抢中选手对后面抢中选手的屏蔽作用,这里设置了enable和selen两个内部标志寄存器。在抢答开始之前两个标志寄存器都为0,选手抢答被禁止,当主持人按下start按键后,“允许抢答”寄存器enable置1,“已抢完”寄存器selen仍为0,此时允许选手抢答。当有一个选手按键按下后,“已抢完”寄存器senlen被置1,后面的选手抢答又被禁止,禁止一直保持到该轮抢答结束,且主持人按下清零按钮clr。清零后enable和selen两个寄存器都恢复初始值0。
在有选手抢中后,q[3]~q[0]输出四位二进制选手编码并一直保持,同时选手抢中输出信号s由0变为1,表明已有选手抢中,该信号也将一直保持,直到主持人按下清零按钮clr后,选手编码输出和选手抢中输出才恢复为0。
模块实现代码如下:
模块代码:
module encoder(start,clr,p1,p2,p3,p4,q,s);input p1,p2,p3,p4;
input clr,start;
output [3:0] q;
output s;
reg [3:0] q=4'b0000;
reg s=0;
reg enable=0;
reg selen=0;
always @(start or clr or p1 or p2 or p3 or p4)
begin
if(!start)enable<=1; //whenever start key was pressed, the encode is
//enabled
else if(!clr) //when clr key was pressed, the all outputs of encoder are
//get to 0,including the "enable" register
begin
q[3:0]<=4'b0000;
s<=0;
enable<=0;
selen<=0;
end
else if(!p1 || !p2 || !p3 || !p4)
begin
if(enable&&!selen)
begin
selen<=1;
s<=1;
case({p4,p3,p2,p1})
4'b1110:q<=4'b0001; //when No.1 key pressed,the
//s-output is 1,so is the follows
4'b1101:q<=4'b0010;
4'b1011:q<=4'b0011;
4'b0111:q<=4'b0100;
default:q<=q;
endcase
end
end
end
endmodule
3、模块功能仿真结果
编写功能仿真模块如下:
仿真模块代码:module stiencoder;
reg start=1,clr=1,p1=1,p2=1,p3=1,p4=1;
wire s;
wire [3:0] q;
encoder u(start,clr,p1,p2,p3,p4,q,s);
initial
begin
#10 p1=0;
#10 p1=1;
#10 start=0;
#10 start=1;
#20 p2=0;
#10 p2=1;
#20 p1=0;
#10 p1=1;
#20 clr=0;
#10 clr=1;
end
endmodule
功能仿真波形:
图4(二)分频器模块
1、模块功能描述
在这个系统中,不同模块需要不同的时钟频率,因此,需要对主系统时钟进行不同倍数的分频。为了提高时钟信号的质量和系统的可靠性,这里将整个系统所需的不同频率的时钟都放到一起,由同一个模块产生,这就是分频器模块。
在这个系统中,需要时钟信号的模块有蜂鸣器模块、定时器模块和显示译码模块,它们所需的时钟频率分别为1kHz、1Hz和100Hz,因此需要对主时钟进行不同程度的分频,产生三个不同频率的时钟。
分频器模块的逻辑框图如图5所示,主系统时钟由clk
引脚输入,进行不同分频系数的分频后产生三个不同频率的
输出时钟信号,分别为1Hz的clk_1s时钟信号、1kHz的
clk_beep信号和100Hz的clk_sweep信号。
2、模块实现过程
通过对各时钟设定不同的分频系数,就可实现不同时图5
钟频率的输出。在这里,主系统时钟为48MHz,为了获得所需的三种时钟频率,设置clk_1s的分频系数DIV_1S为48000000,设置clk_beep的分频系数DIV_BEEP为48000,设置clk_sweep的分频系数DIV_SWEEP为480000,如此,就可以输出1Hz、1kHz和100Hz的时钟频率。
模块实现代码如下:
模块代码:
module divider(clk,clk_1s,clk_beep,clk_sweep);
input clk;
output clk_1s,clk_beep,clk_sweep;
reg clk_1s=0,clk_beep=0,clk_sweep=0;
parameter DIV_1S=48000000; //1Hz
parameter DIV_BEEP=48000; //1kHz
parameter DIV_SWEEP=480000; //100Hz
integer cnt_1s=0,cnt_beep=0,cnt_sweep=0;
always @(posedge clk)
begin
cnt_1s<=cnt_1s+1;
cnt_beep<=cnt_beep+1;
cnt_sweep<=cnt_sweep+1;if(cnt_1s==DIV_1S)
begin
clk_1s<=~clk_1s;
cnt_1s<=0;
end
else clk_1s<=clk_1s;
if(cnt_beep==DIV_BEEP)
begin
clk_beep<=~clk_beep;
cnt_beep<=0;
end
else clk_beep<=clk_beep;
if(cnt_sweep==DIV_SWEEP)
begin
clk_sweep<=~clk_sweep;
cnt_sweep<=0;
end
else clk_sweep<=clk_sweep;
end
endmodule
3、模块功能仿真结果
编写功能仿真模块如下:
仿真模块代码:
module stidivider;
reg clk=0;
wire clk_1s,clk_beep,clk_sweep;
divider u(clk,clk_1s,clk_beep,clk_sweep);
always #1 clk=~clk;
endmodule
功能仿真波形:
图6(三)蜂鸣器模块
1、模块功能描述
蜂鸣器模块用于驱动蜂鸣器报警。
按照题目要求,抢答开始、选手选中和答题时间到时蜂鸣器都要进行报警,报警时间为100+学号,单位为毫秒,即118ms。
由于有报警时间的要求,因此需要给蜂鸣器模块提供精确的时钟信号。这里选用1kHz的时钟,该时钟信号由分频器模块对主系统时钟进行分频得到。为了实现118ms的定时,只要对1kHz时钟进行计数,计数到118即可。
模块逻辑框图如图7所示。有三种条件可以触发蜂
鸣器报警,即抢答开始、选手选中和定时时间到,这三
个条件成熟时都会有相应的信号输出,抢答开始对应于
主持人开始按钮的下降沿信号,选手选中对应于选手编
码锁存器的选手选中输出信号,定时时间到对应于定时
器的定时时间到信号,这三个信号分别通过start端口、
s端口和stop端口与蜂鸣器模块相连。当这三个输入中图7
有一个有效时,输出端out就输出低电平,驱动蜂鸣器报警,直到118ms时间到。计时时钟从clk端口输入。
2、模块实现过程
为了实现对蜂鸣器的控制,即在触发信号有效后的118ms时间内输出低电平驱动蜂鸣器报警,在这个模块中设置了两个内部标志寄存器flag_start和flag_stop,取这两个寄存器的异或非作为输出。
flag_start寄存器和flag_stop寄存器的初始值都设为0,此时两个寄存器的异或非输出为1,蜂鸣器不报警;当有一个触发信号有效时,“报警开始”寄存器flag_start取反,而“报警停止”寄存器flag_stop保持不变,此时两个寄存器的异或非输出为0,蜂鸣器开始报警,同时内部计数器开始对输入的1kHz 时钟进行计数;当计数值到118时,“报警停止”寄存器取反,而“报警开始”寄存器保持不变,此时两个寄存器的异或非输出又为1,蜂鸣器报警停止。
通过在报警开始和报警结束时分别将flag_start和flag_stop寄存器取反,使它们在报警期间值不同,不报警期间值相同,这样就可实现报警期间输出为0,不报警期间输出为1,正确实现对蜂鸣器的控制。
模块实现代码如下:
模块代码:
module beeper(s,start,stop,clk,clr,out);
input s,start,stop,clk,clr;
output out;
reg flag_start=0,flag_stop=0;
integer cnt=0;
xnor B(out,flag_start,flag_stop);
always @(s or stop or start or clr)
begin
if(!clr)flag_start<=0;
else if(s || !start || stop)flag_start<=~flag_start;
end
always @(posedge clk or negedge clr)
begin
if(!clr)
begin
flag_stop<=0;
cnt<=0;
end
else if(!out)
begin
cnt<=cnt+1;
if(cnt==118)
begin
flag_stop<=~flag_stop;
cnt<=0;
end
else flag_stop<=flag_stop;
end
else cnt<=cnt;
end
endmodule
3、模块功能仿真结果
编写功能仿真模块如下:
(1)由抢答开始信号触发:
仿真模块代码:
module stibeeper;reg s,start,stop,clk,clr;
wire out;
beeper u(s,start,stop,clk,clr,out);
initial
begin
s=0;
start=1;
stop=0;
clk=0;
clr=1;
#10 start=0;
#10 start=1;
#260 clr=0;
#10 clr=1;
end
always #1 clk=~clk;
endmodule
功能仿真波形:
图8 (2)由选手选中信号触发:
仿真模块代码:
module stibeeper;
reg s,start,stop,clk,clr;
wire out;
beeper u(s,start,stop,clk,clr,out);
initial
begin
s=0;
start=1;stop=0;
clk=0;
clr=1;
#10 s=1;
#10 s=0;
#260 clr=0;
#10 clr=1;
end
always #1 clk=~clk;
endmodule
功能仿真波形:
图9 (3)由定时时间到信号触发:
仿真模块代码:
module stibeeper;
reg s,start,stop,clk,clr;
wire out;
beeper u(s,start,stop,clk,clr,out);
initial
begin
s=0;
start=1;
stop=0;
clk=0;
clr=1;
#10 stop=1;
#10 stop=0;
#260 clr=0;
#10 clr=1;end
always #1 clk=~clk;
endmodule
功能仿真波形:
图10
(四)定时器模块
1、模块功能描述
在选手抢中后,按照题目规定,只有10s的答题时间,因此需要对选手答题进行定时。定时器模块就是用于为选手答题计时,并实时输出所剩余的时间。
由于有定时时间的要求,因此和蜂鸣器模块一样,也需要为定时器模块提供精确的时钟信号。这里选用1Hz的时钟,该时钟信号也是由分频器模块对主系统时钟进行分频得到。为了实现10s的定时,只要对1Hz时钟进行计数,计数到10即可。
模块逻辑框图如图11所示。定时器计时由选手抢中信
号触发,该信号从s端口输入;q[3]~q[0]实时输出当前剩
余答题时间的四位二进制编码;当定时器倒计时到零,即
答题时间到时,stop输出端输出一个定时时间到信号。计
时时钟信号从clk端口输入。
图112、模块实现过程
为了控制定时器在规定的时间定时,在这个模块中设置了一个内部标志寄存器enable,只有当enable为1时,定时器才开始倒计时。
当倒计时触发信号,即选手抢中信号到来时,“倒计时允许”寄存器enable 置1,此时倒计时开始,内部计数器开始对输入的1Hz时钟进行计数,同时,在当前剩余时间输出端口q[3]~q[0]输出当前剩余时间的四位二进制编码;当倒计时到0时,定时时间到输出端stop输出1,同时“倒计时允许”寄存器enable 恢复为0,计时终止。
模块实现代码如下:
模块代码:
module counter(s,clr,clk,stop,q);
input s,clr,clk;
output [3:0] q;
output stop;
reg [3:0] q=4'b1010;
reg stop=0;
reg enable=0;
always @(posedge s or posedge stop)
begin
if(s)enable<=1;
if(stop)enable<=0;
end
always @(posedge clk or negedge clr)
begin
if(!clr)stop<=0;
else if(enable)
begin
if(q==0)stop<=1;
else
begin
stop<=stop;
q<=q-4'b0001;
end
end
else if(!enable)q<=4'b1010;
else stop<=stop;
end
endmodule3、模块功能仿真结果
编写功能仿真模块如下:
仿真模块代码:
module sticounter;
reg s,clr,clk;
wire stop;
wire [3:0] q;
counter u(s,clr,clk,stop,q);
initial
begin
s=0;
clr=1;
clk=0;
#10 s=1;
#15 s=0;
#15 clr=0;
#10 clr=1;
end
always #1 clk=~clk;
endmodule
功能仿真波形:
图12
(五)显示译码模块
1、模块功能描述
在这个系统中,需要用数码管显示抢中选手的编号和选手答题倒计时时间,为了将四位二进制编码转换为数码管能识别的八位(或七位)显示代码,需要八段(或七段)译码器,这就是显示译码模块。
模块逻辑框图如图13所示。四位二进制编码由四位
数据输入端d[3]~d[0]输入,八位显示代码由八段码输出
端seg[7]~seg[0]输出。为了方便多位数码管时实现动态
扫描显示,在这个模块中还设置了使能输入端en,当en
为0时,八段码输出端无效,输出全0,当en为1时,
八段码输出端按照四位输入数据输出相应的显示代码。图13
2、模块实现过程
为了实现译码功能,这里使用了一个case分支表。
当使能输入端en为1时,根据输入的四位二进制代码,八段码输出端输出相应的八位显示代码,当使能输入端en为0时,八段码输出端输出0。
模块实现代码如下:
模块代码:
module decoder(d,en,seg);
input [3:0] d;
input en;
output [7:0] seg;
reg [7:0] seg;
always @(d or en)
begin
if(!en)seg=8'b00000000;
else
begin
case(d)
4'b0000:seg=8'b00000011; //0
4'b0001:seg=8'b10011111; //1
4'b0010:seg=8'b00100101; //2
4'b0011:seg=8'b00001101; //3
4'b0100:seg=8'b10011001; //4
4'b0101:seg=8'b01001001; //54'b0110:seg=8'b01000001; //6
4'b0111:seg=8'b00011111; //7
4'b1000:seg=8'b00000001; //8
4'b1001:seg=8'b00001001; //9
4'b1010:seg=8'b00000011; //0
endcase
end
end
endmodule
3、模块功能仿真结果
为了方便对仿真波形进行截图,这里将en=0和en=1两种情况分开来进行仿真。
编写功能仿真模块如下:
(1)en=0:
仿真模块代码:
module stidecoder;
reg [3:0] d;
reg en;
wire [7:0] seg;
decoder u(d,en,seg);
initial
begin
d=4'b0000;
en=0;
#10 d=4'b0001;
#10 d=4'b0010;
#10 d=4'b0011;
#10 d=4'b0100;
#10 d=4'b0101;
#10 d=4'b0110;
#10 d=4'b0111;
#10 d=4'b1000;
#10 d=4'b1001;
#10 d=4'b1010;
end
endmodule功能仿真波形:
图14 (2)en=1:
仿真模块代码:
module stidecoder;
reg [3:0] d;
reg en;
wire [7:0] seg;
decoder u(d,en,seg);
initial
begin
d=4'b0000;
en=1;
#10 d=4'b0001;
#10 d=4'b0010;
#10 d=4'b0011;
#10 d=4'b0100;
#10 d=4'b0101;
#10 d=4'b0110;
#10 d=4'b0111;
#10 d=4'b1000;
#10 d=4'b1001;
#10 d=4'b1010;
endendmodule
功能仿真波形:
图15
(六)顶层模块
1、模块功能描述
顶层模块用于将系统中各个子模块连接起来,形成一个完整的系统,并引出与外围电路的连接端口。
整个顶层模块的逻辑框图如图16所示。模块有start、clr、clk、p1、p2、p3、p4六个输入端口,beep、seg[7]~seg[0]、selbit[2]~selbit[0]12个输出端口。主系统时钟clk输入到分频器模块u2,由分频器模块进行分频产生3个不同频率的时钟输出,分别是1Hz的clk_1s、1kHz的clk_beep和100Hz的clk_sweep,这三个时钟信号分别提供给定时器模块定时、蜂鸣器模块报警时间定时和数码管动态显示。主持人开始按钮连接到选手编码锁存器模块u1和蜂鸣器模块u3,当主持人按下开始按钮时,抢答开始,选手编码锁存器模块u1接收到主持人抢答开始信号后,将内部“抢答允许”标志寄存器置1,允许选手抢答,蜂鸣器模块u3接收到主持人抢答开始信号后,开始118ms的报警。选手编码锁存器模块u1内部“抢答允许”标志寄存器置1后,等待p1~p4四个选
图16
手按键按下,当监测到第一个选手按下时,内部“已抢完”寄存器置1,阻止后面的选手抢答,同时,将抢中选手编号的四位二进制代码从q[3]~q[0]输出给显示译码器u5进行译码,显示抢中选手编号,选手抢中输出端s输出选手抢中信号。选手抢中信号连接到蜂鸣器模块u3和定时器模块u4。蜂鸣器模块u3接收到选手抢中信号后,又开始一次118ms的报警。定时器模块u4接收到选手抢中信号后,开始10s的答题倒计时,同时,将当前剩余时间的四位二进制代码从q[3]~q[0]输出给显示译码器u6进行译码,显示当前剩余时间;当倒计时到0时,定时时间到输出端输出定时时间到信号,传送给蜂鸣器模块u3。蜂鸣器模块u3接收到定时时间到信号后,开始第三次118ms的报警。显示译码模块u5和u6的使能端分别由clk_sweep时钟及其取反后的时钟控制,因此,在同一时间,只有一个显示译码模块输出有效的显示代码,另一个显示译码模块则输出0,这样,在两个显示译码模块的输出端分别对对应的段码位取或,就可获得当前有效的显示译码模块输出的显示代码;clk_sweep时钟还同时驱动位选端selbit[0],selbit[2]~selbit[0]连接到外围电路的用于数码管位选的3-8译码器的三个输入端,这里使用3-8译码器Y0、Y1两个输出端所连接的两个数码管,因此,在clk_sweep时钟的作用下,Y0、Y1两个数码管将轮流被选中,当clk_sweep为0时,Y0被选中,同时,显示译码模块u6有效,段码输出端输出u6的显示代码,即定时器倒计时时间,所以,Y0数码管显示倒计时时间,当clk_sweep为1时,Y1被选中,同时,显示译码模块u5有效,段码输出端输出u5的显示代码,即抢中选手编号,所以,Y1数码管显示抢中选手编号,如此,实现了两位数码管的动态显示。主持人清零按钮clr连接到选手编码锁存器模块u1、蜂鸣器模块u3和定时器模块u4,用于将系统恢复到初始状态。2、模块实现过程
使用Verilog语言中的结构描述语句,对前面构建的各种子模块进行实例化,通过内部各种信号连接,组成一个完整的系统。
模块实现代码如下:
模块代码:
module qiangdaqi(p1,p2,p3,p4,start,clr,clk,seg,beep,selbit);
input p1,p2,p3,p4,start,clr,clk;
output beep;
output [2:0] selbit;
output [7:0] seg;
wire select,stop,clk_beeper,clk_cnt,clk_sweep,clk_sweepn;
wire [3:0] qp,qc;
wire [7:0] segp,segc;
encoder u1(start,clr,p1,p2,p3,p4,qp,select);
divider u2(clk,clk_cnt,clk_beeper,clk_sweep);
beeper u3(select,start,stop,clk_beeper,clr,beep);
counter u4(select,clr,clk_cnt,stop,qc);
decoder u5(qp,clk_sweep,segp);
decoder u6(qc,clk_sweepn,segc);
not A0(clk_sweepn,clk_sweep);
or A[8:1] (seg,segp,segc);
assign selbit[0]=clk_sweep;
assign selbit[1]=0;
assign selbit[2]=0;
endmodule
3、模块功能仿真结果
编写功能仿真模块如下:
仿真模块代码:
module stiqiangdaqi;
reg p1,p2,p3,p4,start,clr,clk;
wire [7:0] seg;
wire beep;
wire [2:0] selbit;
qiangdaqi u(p1,p2,p3,p4,start,clr,clk,seg,beep,selbit);
initial
begin
p1=1;
p2=1;
p3=1;
p4=1;
start=1;
clr=1;
clk=0;
#5 p1=0;
#5 p1=1;
#5 start=0;
#5 start=1;
#50 p3=0;
#5 p3=1;
#5 p1=0;
#5 p2=0;
#5 p4=0;
#5 p1=1;
p2=1;
p4=1;
#600 clr=0;
#5 clr=1;
end
always #1 clk=~clk;
endmodule功能仿真波形:
图17
为了便于截图,在仿真时将分频器模块中的三个分频系数DIV_1S、DIV_BEEP、DIV_SWEEP分别改成了10、1、5。
五、下载和调试
在这个实验中使用的是XILINX的FPGA开发板,用ISE开发XILINX FPGA 的流程如图18所示。
图18
当编写完各功能模块并编译、综合、仿真成功后就可以进行下载调试了。
在下载前需要对器件的管脚进行分配,p1、p2、p3、p4、start、clr分别与六个按键相连,clk与晶振相连,beep与蜂鸣器相连,seg[7]~seg[0]分别于数码管的a~h相连,selbit[2]~selbit[0]分别与数码管位选端的3-8译码器的输入端C、B、A相连。
将最终生成的*.bit文件下载到FPGA之后就可以进行调试了,根据系统运行的结果判断程序是否有问题,如果有问题,根据问题的特点检查与之相关的模块,并逐一予以修正,直到系统运行结果满足要求为止。
六、总结
在这个实验中,我们利用FPGA实现了一个抢答器的设计。通过这次实验,我有了很大的收获。
首先,通过这个实验,我复习巩固了数字逻辑的一些知识,真正体验了一次数字逻辑设计。作为一个异步时序逻辑,在这个抢答器的设计过程中,我遇到过各种各样的困难,有些也涉及到了数字逻辑课上学过的竞争冒险方面的问题,利用以前学过的知识,以及硬件描述语言中一些常用的编程原则,我将这些问题都一一解决了。
其次,通过这次实验,我学会了Verilog语言和FPGA的使用方法。在这以前,我曾经学过一些Verilog语言,但没有实际使用过,对于FPGA,也只是用原理图编辑方式进行过一些简单的设计,因此,对于用Verilog语言对FPGA进行编程需要注意些什么,我一点都不了解,甚至连哪些语句是可综合的都不知道。之前,习惯了用C语言对单片机进行编程,所以,当我第一次使用Verilog语言对FPGA进行编程时,完全按照了C语言的编程思维,用描述性语句将系统描述了一遍,到综合的时候才发现,自己写的这些语句硬件根本没法实现,这时候才认识到可综合语句和可综合编程原则的重要性。
再次,在这个系统的设计过程中,我掌握了一些提高数字系统可靠性的设计原则,包括在组合逻辑和时序逻辑中的一些设计原则。例如,在Verilog中,过程赋值语句有阻塞型赋值语句和非阻塞型赋值语句之分,在我最初的设计中,我没有认识到两者的区别,全部用阻塞型赋值语句,在调试时出现了很多错误,后来,我按照“组合逻辑用阻塞型赋值语句,时序逻辑用非阻塞型赋值语句”这个原则,使问题减少了很多。另外,还有很多关于避免竞争冒险、组合环路等方面的编程原则,按照这些原则进行系统程序设计,可以在很大程度上提高系统的可靠性,同时,也可以使调试更加顺利。
最后,也是以前在参加电子设计竞赛时所认识到的,就是调试时一定要有耐心。电路设计中遇到问题是很正常的事情,在遇到问题时,要认真分析问题出现的原因,寻找问题的出处,并将它解决。
