一、目的
1、熟悉Modelsim 软件
2、掌握Modelsim软件的编译、仿真方法
3、熟练运用Modelsim软件进行HDL程序设计开发
4、电子表的设计,包括正常计时模块,LED显示模块,定时报警模块,校时模块,秒表模块。
二、原理与方法:
应用Verilog 语言编写频率可调的四种波形发生的程序,加深了解Verilog 的编程以及用modlesim进行波形仿真的能力,并通过QUARTUS II进行时序仿真回顾QUARTUE II 的操作,加强对这两种软件的实际应用操作能力,同时将这门实验课的理论基础和实践联系起来,培养学生的实践能力和应用能力。
Verilog是一种硬件描述语言:用程序设计语言来描述数字电路的功能,提高设计效率。早期数字电路设计是基于原理图输入的,类似电路板的PCB原理图设计。电子表的设计包括正常计时模块,LED显示模块,定时报警模块,校时模块,秒表模块。
Verilog语法:
可综合描述:综合tool能够把verilog描述转化(compile)成基本的数字电路底层cell(与或非gate,寄存器等)的描述。
不可综合描述:综合tool不能把verilog描述转换为基本的数字电路底层cell的描述。
verilog设计仿真与实现:通过EDA tool,可以在计算机上对verilog设计的功能进行仿真。
常用的仿真工具(所有verilog描述):Modelsim/Questasim(Mentor)通过模块module的层次化设计,实现一个复杂的数字逻辑功能。
标识符:
用于定义code中的各种名字,比如:信号,module,define,parameter;
标识符由:字符,数字,下划线组成;
首字母必须是字母或者下划线;
标识符是区分大小写的;
信号名字建议与信号功能相对应:rstn,clk_core,mem_cs,mem_we,mem_dout,mem_din,mem_addr;
数据物理类型:
线性数据:用于连续赋值语句(assign)描述组合逻辑或者module间的信号连接线。
1. Verilog HDL程序是由模块构成的。每个模块嵌套在module和endmodule声明语句中。
2 每个Verilog HDL源文件中只有一个顶层模块,其他为子模块。可以每个模块写一个文件。
3.每个模块要进行端口定义,并说明输入输出端口,然后对模块的功能进行行为逻辑描述。
4.模块中的时序逻辑部分在always块的内部,在always块中只能对寄存器变量赋值。
5.模块中对端口或其他wire型变量的赋值,必须在always块的外部使用assign语句,通常是将寄存器的值送出
6.程序书写格式自由,一行可以写几个语句,一个语句也可以分多行写。
7. 除了endmodule语句、begin_end语句和fork_join语句外,每个语句和数据定义的最后都要加分号
8.可用/*.....*/和//...对程序的任何部分作注释。加上必要的注释,可以增强程序的可读性
时钟生成模块
这里需要注意的一个地方,通常我们见到的时间都是十进制的,但是我们用计数器计数之后输出的数据通常为16进制数。
十进制数显示的范围为0~9;
十六进制数显示的范围为0~f;我们需要的数只有0到9,所以当数超过9时对于我们的电子表来说是多余的。因此我们需要对十六进制数进行转换,让输出的数(也就是数码管上显示的数)为十进制。
其转换原理为:
对应十进制数大于9时,进行加6操作;
对应十进制数大于19时,进行加12操作;
对应十进制数大约29时,进行加18操作;
以此类推···
其转换的核心思想是将二进制数转化为BCD码
计数器部分中包含有三个主要计数部分,分别是十进制、六进制以及二十四进制,其中六进制和十进制共同组成六十进制,即实现分和秒的计数,之所以将其分开是便于分别显示个位和十位,通过编写计数器,来计数信号的数量,从而实现时分秒按各自的进制正常计数,同时,将前一时钟单位的进位信号作为下一时钟单位的clk,即从后向前驱动,这样便实现了时钟的正常运转。
二、内容:
(1)正常计时模块clock
module clock(clk,rst,clock_en,second,minute,hour);
input clk,rst,clock_en;
output[5:0]second,minute,hour;
reg[5:0]second,minute,hour;
always@(posedge clk or negedge rst or posedge clock_en)
if(!rst)
begin
second<=0; minute<=0; hour<=0;
end
else if(clock_en)
begin
if(second==59)
begin
minute<=minute+1;
second<=0;
end
else if(minute==59)
begin
hour<=hour+1;
minute<=0;
end
else if(hour==23)
hour<=0;
else
second<=second+1;
end
endmodule
(2)LED显示模块LED_display
module LED_display(data,high,low);
input[5:0]data;
output[7:0]high,low;
reg[7:0]high,low;
reg[3:0]out_h,out_l;
always@(data)
begin
out_h<=data/10;
out_l<=data;
end
always@(out_l)
case(out_l)
4'b0000:low<=8'b00000000;
4'b0001:low<=8'b01100000;
4'b0010:low<=8'b11011010;
4'b0011:low<=8'b11110010;
4'b0100:low<=8'b01100110;
4'b0101:low<=8'b10110110;
4'b0110:low<=8'b10111110;
4'b0111:low<=8'b11100000;
4'b1000:low<=8'b11111110;
4'b1001:low<=8'b11110110;
default:low<=8'b00000000;
endcase
always@(out_h)
case(out_h)
4'b0000:high<=8'b00000000;
4'b0001:high<=8'b01100000;
4'b0010:high<=8'b11011010;
4'b0011:high<=8'b11110010;
4'b0100:high<=8'b01100110;
4'b0101:high<=8'b10110110;
4'b0110:high<=8'b10111110;
4'b0111:high<=8'b11100000;
4'b1000:high<=8'b11111110;
4'b1001:high<=8'b11110110;
default:high<=8'b00000000;
endcase
endmodule
(3)定时模块timing和报警模块alarm
module timing(rst,timing_en,hour,minute,hour,location_adjust,timing_adjust,hour_timing,minute_timing,secong_timing,out);
input rst,timing_en,location_adjust;
input[5:0]minute,hour,second;
output[5:0]second_timing,minute_timing,hour_timing;
reg[5:0]second_timing,minute_timing,hour_timing;
output out;
reg[1:0]location
always@(negedge rst or posedge timing_en or posedge location_adjust)
if(!rst)
begin
second_timing<=0; minute_timing<=0; hour_timing<=0;location<=0;
end
else if(location_adjust==1)
begin
if(location==2'b10)
location<=2'b00;
else
location<=location+1;
end
else if(timing_en==1)
begin
if(location==2'b00)
hour_timing<=hour_timing+1;
else if(location==2'b01)
minute_timing<=minute_timing+1;
else if(location==2'b10)
second_timing<=second_timing+1;
else
begin
second_timing<=second_timing;
minute_timing<=minute_timing;
hour_timing<=hour_timing;
end
end
assign out=(hour_timing==hour && minute_timing==minute && second_timing==second)?1:0;
endmodule
//报警模块alarm
module alarm(clk,rst,alarm_en,alarm_out);
input clk,rst,alarm_en;
output alarm_out;
reg alarm_out;
reg[4:0] count;
always@(posedge clk or negedge rst)
if(!rst)
begin
alarm_out<=0;
count<=0;
end
else if(alarm_en==1)
begin
if(count==29)
begin
count<=0;
alarm_out<=0;
end
else
begin
count<=count+1;
alarm_out<=1;
end
end
else
begin
count<=0;
alarm_out<=0;
end
endmodule
(4)校时模块adjust_time
module adjust_time(rst,adjust_en,location_adjust,second,minute,hour,second_adjust,minute_adjust,hour_adjust);
input rst,adjust_en,location_adjust;
input[5:0]second,minute,hour;
output[5:0]second_adjust,minute_adjust,hour_adjust;
reg[5:0]second_adjust,minute_adjust,hour_adjust;
reg[1:0]location
always@(posedge adjust_en or location_adjust or negedge rst)
if(!rst)
begin
second_adjust<=0;minute_adjust<=0;hour_adjust<=0;location<=0;
end
else if(location_adjust)
begin
if(location==2'b10)
location<=2'b00;
else
location<=location+1;
end
else if(adjust_en==1)
begin
case(location)
2'b00: hour_adjust<=hour_adjust+1;
2'b01: minute_adjust<=minute_adjust+1;
2'b10: second_adjust<=second_adjust+1;
default:
begin
hour_adjust<=0;
minute_adjust<=0;
second_adjust<=0;
end
endcase
end
endmodule
//(5)秒表模块second_watch
module second_watch(clk,rst,watch_en,stop,minute,second);
input clk,rst,watch_en,stop;
output[5:0]minute,second;
reg[9:0]dout;
always(posedge clk or negedge rst)
if(!rst)
dout<=0;
else if(watch_en)
begin
if(stop==1)
dout<=dout;
else if(dout==999&&stop==1)
dout<=0;
else
dout<=dout+1;
end
assign minute=dout/60;
assign second=dout`;
endmodule
// (6)计时,报警,校时,秒表状态的转换
module state(clk,rst,c0,c1,c2,c3,clock_en,timing_en,adjust_en,watch_en);
input clk,rst,c0,c1,c2,c3;
output clock_en,timing_en,adjust_en,watch_en;
reg clock_en,timing_en,adjust_en,watch_en;
reg[3:0]state,next_state;
parameter s0=4'b0000,s1=4'b0001,s2=4'b0010,s3=4'b0011;
always@(posedge clk)
if(!rst)
state<=s0;
else
state<=next_state;
always@(state or rst or c0 or c1 or c2 or c3)
begin
if(!rst)
next_state<=s0;
else
begin
case(state)
s0: begin
if(c1)
next_state<=s1;
else if(c2)
next_state<=s2;
else if(c3)
next_state<=s3;
else
next_state<=s0;
end
s1: begin
if(c1)
next_state<=s1;
else if(c2)
next_state<=s2;
else if(c3)
next_state<=s3;
else
next_state<=s0;
end
s2: begin
if(c1)
next_state<=s1;
else if(c2)
next_state<=s2;
else if(c3)
next_state<=s3;
else
next_state<=s0;
end
s3: begin
if(c1)
next_state<=s1;
else if(c2)
next_state<=s2;
else if(c3)
next_state<=s3;
else
next_state<=s0;
end
default:next_state<=s0;
endcase
end
end
always@(state)
begin
case(state)
s0: begin (计时)
clock_en<=1;
timing_en<=0;
adjust_en<=0;
watch_en<=0;
end
s1: begin(定时报警)
clock_en<=1;
timing_en<=1;
adjust_en<=0;
watch_en<=0;
end
s2: begin(校时)
clock_en<=0;
timing_en<=0;
adjust_en<=1;
watch_en<=0;
end
s3: begin(秒表)
clock_en<=1;
timing_en<=0;
adjust_en<=0;
watch_en<=1;
end
default:begin
clock_en<=1;
timing_en<=0;
adjust_en<=0;
watch_en<=0;
end
endcase
end
endmodule
// (7)电子表顶层模块dianzibiao_top
//`include"adjust_time.v"
//`include"alarm.v"
//`include"clock.v"
//`include"LED_display.v"
//`include"second_watch.v"
//`include"state.v"
//`include"timing.v"
//`include"top.v"
module dianzibiao_top( clk,rst,c0,c1,c2,c3,stop,location,hour_h,hour_l,minute_h,minute_l,second_h,second_l,alarm_out);
input clk,rst,c0,c1,c2,c3,stop,location;
output[7:0]hour_h,hour_l,minute_h,minute_l,second_h,second_l;
output alarm_out;
wire[5:0]second,minute,hour,second_clock,minute_clock,hour_clock,hour_LED,minute_LED,second_LED,sec_timing,min_timing,hour_timing,sec_adjust,min_adjust,hour_adjust,minute_adjust,second_adjust;
wire clock_en,alarm_en,timing_en,adjust_en,watch_en;
wire[5:0]minute_w,second_w;
clock clock1(.second(second_clock),.minute(minute_clock),.hour(hour_clock),.clock_en(clock_en),.clk(clk),.rst(rst));
LED_display LED_display1(.data(hour_LED),.high(hour_h),.low(hour_l));
LED_display LED_display2(.data(minute_LED),.high(minute_h),.low(minute_l));
LED_display LED_display3(.data(second_LED),.high(second_h),.low(second_l));
alarm alarm1(.alarm_out(alarm_out),.alarm_en(alarm_en),.clk(clk),.rst(rst));
timing timing1(.rst(rst),.timing_en(timing_en),.location_adjust(location),.hour(hour_clock),.minute(minute_clock),.second(second_clock),.second_timing(sec_timing),.minute_timing(min_timing),.hour_timing(hour_timing),.out(alarm_en));
adjust_time adjust_time1(.rst(rst),.adjust_en(adjust_en),.location_adjust(location),.hour(hour_clock),.minute(minute_clock),.second(second_clock),.second_adjust(sec_adjust),.minute_adjust(min_adjust),.hour_adjust(hour_adjust));
second_watch second_watch1(.watch_en(watch_en),.clk(clk),.rst(rst),.stop(stop),.minute(minute_w),.second(second_w));
state state1(.clk(clk),.rst(rst),.c0(c0),.c1(c1),.c2(c2),.c3(c3),.clock_en(clock_en),.timing_en(timing_en),.adjust_en(adjust_en),.watch_en(watch_en));
assign hour=c1?hour_timing:hour_clock;
assign minute=c1?min_timing:minute_clock;
assign second=c1?sec_timing:second_clock;
assign minute=c3?minute_w:minute_clock;
assign second=c3?second_w:second_clock;
assign hour=c2?hour_adjust:hour_clock;
assign minute=c2?min_adjust:minute_clock;
assign second=c2?sec_adjust:second_clock;
assign hour_LED=hour;
assign minute_LED=minute;
assign second_LED=second;
endmodule
运行结果:
秒分进位:
时分进位:
整点报时:
Testbench:
1.编写各个模块的VHDL代码并进行编译与波形仿真,仿真无误后生成元件符号。
2.设计数字钟电路的顶层文件,在顶层文件中调入第一步中生成的元件符号,并根据连接关系将它们连接在一起。
3.引脚分配,为顶层设计文件中的各个输入输出端口分配芯片相应的引脚。4.下载程序到芯片,观看实验现象是否为预想的那样。同时使用清零按键看能否实现清零,时间正常走动情况下通过按键能否实现校时。
下载成功后,拨动开关 DP4至高电平,使六个数码管复位清零;拨动开关 DP4至低电平,数字钟开始自动计,此过程中可以通过1键设置小时数,2键设置分钟数。当秒数满60则进一位,分钟数满60进一位,当显示为23:59:59 时,秒数在加一则显示 00:00:00,同时指示一天结束的LED灯亮 10秒,之后从新计时。