利用Verilog HDL规划一款电子表芯片(万年历)

规划一款电子表芯片,能够显示年月日,星期,并且实现闰年的自动调整,钟振32768Hz。

要求:增加测试设计,快速覆盖400年周期

目的:

  1. 掌握Verilog语言对组合逻辑的描述
  2. 学习testbench的设计方法
  3. 掌握仿真器(modelsim/Verilog/VCS)的仿真、调试、波形输出等常用技巧
  4. 掌握DC约束规划方法、综合器使用、结果查看、后仿真等
  5. 熟悉简单芯片从规划到实现方法

设计思路:

  • 400年周期设定为1900年1月1日-2299年12月31日。
  • 以天(day)为单位,每七天进位(carry)一星期(week),判断月份,确定每月的天数(number),到每个月最后一天进位月,月满12个月进位年,年到2199进位变为1900。
  • 钟振32768Hz,2^15 = 32768,所以 32768Hz 16分频后变为 1 Hz,周期即为1秒。测试时若设置为32768Hz,在一个周期内即可跑32768秒,一般晶振采用32768Hz便于分频。此例以天为单位,测试时1s即可跑32768天,5s即可跑完400年,但这种测试方法过于笨重。本实验不做硬件设计因此不考虑钟振。
  • 检测闰年没有找到较好方法,由于本例只有400年周期,因此按如下所示方法进行检测
assign year_4 = (year[1:0]==2'b0);
assign year_100 = (year == 1900)||(year == 2100)||(year == 2200);
assign year_400 = (year == 2000);

if((year_4&&!year_100)||year_400)    
    number=29;
else                                 
    number=28;

 文件代码如下:

module calendar(  
							// 时钟、复位、设置时间    
							input clk , rst , time_set,  
							// 设置时间的4个参数
							input[11:0] year_set,   
							input[3:0]   mon_set,   
							input[2:0]   week_set,  
							input[4:0]   day_set,   
							// 输出显示日历,年份最大是2299年,因此使用2^12 = 4096,12位寄存器存储年份信息
							output reg[11:0] year,   
							output reg[3:0] mon,   
							output reg[2:0] week,  
							output reg[4:0] day,   
							// 月进位和年进位
							output reg mon_carry , year_carry
);
	// 设置每个月的天数
	reg[4:0] number;  
	wire year_4;
	wire year_100;
	wire year_400;
	// 判断是否闰年
	assign  year_4 = (  year[1:0]==2'b0 );
	assign  year_100 = ( year == 1900 ) || ( year == 2100 ) || ( year == 2200 );
	assign  year_400 = ( year == 2000 );
// 每当设置时间或时钟上升沿到来,以天为单位,天数+1,加到这个月的天数,输出月进位信号
    always@(posedge time_set or posedge clk or negedge rst)    
        begin
            if(rst)
				begin
					day<=1;
				end
            else if(time_set)
				begin 
					day<=day_set;
					mon_carry<=0;
				end
            else if(day==number)
				begin
					day<=1;
					mon_carry<=1;
				end
            else 
                begin
					day<=day+1;
					mon_carry<=0;
				end
		end
// 星期数逐天增加,到星期日后回到星期一
    always@(posedge time_set or posedge clk or negedge rst)  
        begin
				if(rst)
					begin
						week<=6;
					end
				else if(time_set)
					begin
						week<=week_set;
					end
				else if(week==7)
					begin
						week<=1;
					end
				else
					begin
						week<=week+1;
					end  
		end   
// 每当月进位信号来临,月份+1		
	always@(posedge mon_carry or posedge time_set or negedge rst)  
        begin
            if(rst)
				begin
					mon<=1;
				end
            else if(time_set)
				begin
					mon<=mon_set;
					year_carry<=0;
				end
            else if(mon==12)
				begin
					mon<=1;
					year_carry<=1;
				end
            else
				begin
					mon<=mon+1;
					year_carry<=0;
				end 
		end
// 每当年进位信号来临,年份+1		
	always@(posedge year_carry or posedge time_set or negedge rst) 
        begin
            if(rst)
                begin
                    year<=1900;
                end
            else if(time_set)
                begin
					year<=year_set;
                end
            else if(year==2299)              
				begin
					year<=1900;        
				end
            else 
				begin
					year<=year+1;               
				end
		end
// 每当月份发生变化,将每个月的天数赋值给number
	always@(*)
        begin 
			case(mon) 
				1 : number=31; 
				2 : 
					begin 
						if ( ( year_4 && ! year_100 ) || year_400 )
							number=29;
						else
							number=28;
					end
				3 : number=31;
				4 : number=30;
				5 : number=31; 
				6 : number=30;
				7 : number=31;
				8 : number=31; 
				9 : number=30;
				10 : number=31;
				11 : number=30; 
				12 : number=31;
				default: number=31;
			endcase 
		end     
endmodule

测试代码如下:

// 仿真测试时间间隔和真实时间没有任何关系,可以随便设置
`timescale 1ns/1ns    
module calendar_tb;
	reg clk , rst , time_set;
	reg[11:0] year_set;  
	reg[3:0] mon_set;  
	reg[2:0] week_set; 
	reg[4:0] day_set;  
	wire[11:0] year;  
	wire[3:0] mon;  
	wire[2:0] week; 
	wire[4:0] day;  
	wire mon_carry,year_carry;
// 实例化
	calendar  v1(
						.clk(clk),
						.time_set(time_set),
						.rst(rst),    
						.year_set(year_set), 
						.mon_set(mon_set),  
						.week_set(week_set), 
						.day_set(day_set),  
						.year(year),                
						.mon(mon),
						.week(week),
						.day(day),
						.year_carry(year_carry),                 
						.mon_carry(mon_carry)
						);
// 开始测试,#10=1天,#300=1月,#100 = 一周多
	initial 
		begin
			clk=0;
			forever #5 clk=~clk; 
		end
	initial
		begin
			time_set=0; 
			rst=0;
			#10 rst=1;
			#10 rst=0;
			#300 time_set=1;
			#1    time_set=0;
			#300 time_set=1;
			#1    time_set=0;
			#300 time_set=1;
			#1    time_set=0;
			#300 time_set=1;
			#1    time_set=0;
		end
	initial
		begin 
			year_set=1900;  
			mon_set=1;  
			week_set=1; 
			day_set=1;  
			#320; 
			year_set=1900;  
			mon_set=2;  
			week_set=3; 
			day_set=28;  
			#300; 
			year_set=2000;  
			mon_set=2;  
			week_set=1; 
			day_set=28;  
			#300; 
			year_set=2100;  
			mon_set=12;  
			week_set=5;
			day_set=31;  
			#300; 
			year_set=2299;  
			mon_set=12;  
			week_set=5; 
			day_set=31;  
			#300;
			$stop;
		end
endmodule

贴出首次仿真图,测试时间为:

  1. 1900.1.1           1
  2. 1900.2.28         3
  3. 2000.2.28         1
  4. 2100.12.23       5
  5. 2299.12.31       5

利用Verilog HDL规划一款电子表芯片(万年历)_第1张图片

本次实验目标前三点基本达成,本帖完结,功能仿真结束后的代码覆盖率仿真和综合工作内容见下帖。

本文代码参考自https://blog.csdn.net/limanjihe/article/details/46942265

相关文章:

利用Verilog HDL规划一款电子表芯片(万年历)

在modelsim中对万年历进行代码覆盖率仿真

在CentOS中使用Design Compiler (DC) 图形界面/tcl脚本进行电路综合

在DC中使用tcl脚本综合和Formality一致性检查

使用VCS对电路进行后仿真

 

你可能感兴趣的:(EDA应用实践)