Verilog設計—簡易LED數字時鐘

文章目錄

設計方案概述

1、分頻器模塊設計

2、計數器模塊設計

3、LED顯示模塊設計

4、頂層模塊設計(更新中)


設計方案概述

系統設計框圖

本任務設計了一個簡易數字鐘,能實現小時、分鐘和秒的計時及顯示,其中,通過控制時、分和秒實現時鐘計時的計數模塊是本次設計的核心。

計數模塊的關鍵在於能夠理解三個計時單位之間的聯繫,即秒計數滿60產生一個向分鐘的進位,分鐘計數滿60產生一個向小時的進位,這兩個進位信號將小時、分和秒聯繫起來,是理解本設計的關鍵點。

爲時鐘設計一個初值設置控制信號,按下設置信號時能利用開發板上的撥碼開關或按鍵對時間進行校對設置。

  • 任務分析

計數信號來自於開發板提供的時鐘信號,這裏採用的晶振頻率爲50MHz,而秒的計數週期是1秒,因此需要對該時鐘信號進行分頻,因此,分頻器模塊是本任務中另一個重要模塊。

根據常識,“秒”、“分”和“小時”之間存在各自獨立又互相聯繫的計數和進位關係,“秒”和“分”的計數模式相同,每計數滿60個時鐘清零並重新開始計數,相當於一個六十進制的計數器。“小時”的計數模式是每計數滿24個時鐘就進行清零並重新開始計數,相當於一個二十四進制的計數器。所以,六十進制和二十四進制的計數器模塊設計是本任務的核心模塊。

“秒”、“分”和“小時”的計時過程都需要通過LED顯示器進行顯示,所以還需要設計顯示控制模塊。

根據以上分析,本任務需要設計的模塊如下:

  1. 分頻器模塊;

  2. 分、秒和小時的計數器模塊;

  3. 顯示控制模塊。


1、分頻器模塊設計

  • freq_div.v

module freq_div(clk, clk_1);  //clk:輸入時鐘;   clk_1: 輸出時鐘
    input clk;
    output clk_1;
    reg[24:0] counter=25'd0;  //定義計數器,用於計數時鐘(計數25000000,需要用25位計數器)
    reg clk_1=0;
    always@(posedge clk)
		if(counter == 25'h16E35FF) //如果等於24999999
			begin
				counter <= 25'b0;     //把counter恢復成1
				clk_1 <= ~clk_1;      //把clk_1翻轉
			end
		else
			counter <= counter + 1'b1; //counter 繼續計數
endmodule
  • tb_freq_div.v(用於測試)

`timescale 1ns/1ps     

module tb_freq_div();
  reg CLK;
  parameter period=20; //週期爲20*1ns= 1/50MHz
  freq_div T1(.clk(CLK));
 
  initial 
    begin
      CLK= 0;
      forever 
        #(period/2) CLK = ~CLK; //週期爲20us,即輸入時鐘頻率爲50MHz
    end
endmodule
分頻器模塊測試—T1實驗結果

2、計數器模塊設計

信號名

I/O

位寬

含義

clk_1

I

1 bit

分頻後周期爲1s的時鐘輸出

hor

O

5 bits

小時計數結果的輸出(0-23

min

O

6 bits

分鐘計數結果的輸出(0-59

sec

O

6 bits

秒鐘計數結果的輸出(0-59

  • count.v

module count(clk_1, sec, min, hor);

	input clk_1;
	
	reg scarry, mcarry, hcarry;
	
	output reg [4:0] hor=23;
	output reg [5:0] min=51;
	output reg [5:0] sec=49;

	always@(posedge clk_1)
	  begin
			if (sec==6'b111011)    //秒針計數到59
				begin
					 sec <= 6'b000000;
					 scarry <= 1'b1;    //秒向分進位1
				end
			 else
				begin
					 sec <= sec+1'b1;
					 scarry <= 1'b0;
				end
		end

	always@(posedge scarry)
	  begin
			if (min==6'b111011)    //分針計數到59
				begin
					 min <= 6'b000000;
					 mcarry <= 1'b1;    //分向小時進位1
				end
			 else
				begin
					 min <= min+1'b1;
					 mcarry <= 1'b0;
				end
	  end
  
	 always@(posedge mcarry)
	  begin
			if (hor==5'b10111)    //計數到23
					 hor <= 5'b00000;			
			 else
					 hor <= hor+1'b1;
		end

endmodule

  • tb_count.v(用於測試)

`timescale 1ms/1ps

module tb_count();
  reg CLK_1;
  parameter period=1000;
  count T1(.clk_1(CLK_1));
 
  initial 
    begin
      CLK_1= 0;
      forever
        #(period/2) CLK_1 = ~CLK_1;  //週期爲1s,用作測試
    end
endmodule

計數器模塊測試—T2實驗結果

3、LED顯示模塊設計

LED顯示模塊設計

信號名

I/O

位寬

含義

hor

I

5 bits[4:0]

時計數結果,hour

min

I

6 bit[5:0]

分鐘計數結果,minute

sec

I

6 bits[5:0]

秒計數結果,second

horh

O

2 bits

小時高位顯示信號,BCD碼的高兩位爲0

horl

O

4 bit

小時低位顯示信號

minh

O

3 bits

分鐘高位顯示信號,BCD碼的最高位爲0

minl

O

4 bits

分鐘低位顯示信號

sech

O

3 bits

秒高位顯示信號

secl

O

4 bits

秒低位顯示信號

  • led_dis.v

module led_dis(
	hor,min,sec, 
	horh,horl, minh,minl, sech,secl, 
	led_hor_h,led_hor_l, 
	led_min_h,led_min_l, 
	led_sec_h,led_sec_l
	);
	
	input [4:0] hor;
	input	[5:0] min;
	input	[5:0] sec;

	output reg [3:0] horh,horl;
	output reg [3:0] minh,minl;
	output reg [3:0] sech,secl;
	
	output reg [6:0] led_hor_h, led_hor_l;
	output reg [6:0] led_min_h, led_min_l;
	output reg [6:0] led_sec_h, led_sec_l;
	
	integer i;
	
	always@(hor) //將hour的二進制碼轉換成BCD碼,並將十位數、個位數保存至horh、horl
		begin 
			horh=4'd0;
			horl=4'd0;
			for(i=4;i>=0;i=i-1)
				begin	
					if(horh>=5)
						horh=horh+3;
					if(horl>=5)
						horl=horl+3;
					horh = horh<<1;
					horh[0] = horl[3];
					horl = horl<<1;
					horl[0]= hor[i];
				end		
		end	

	always@(min) //將minute的二進制碼轉換成BCD碼,並將十位數、個位數保存至minh、minl
		begin 
			minh=4'd0;
			minl=4'd0;
			for(i=5; i>=0; i=i-1)
				begin	
					if(minh>=5)
						minh=minh+3;
					if(minl>=5)
						minl=minl+3;
					minh = minh<<1;
					minh[0] = minl[3];
					minl = minl<<1;
					minl[0]= min[i];
				end		
		end
	
	always@(sec) //將second的二進制碼轉換成BCD碼,並將十位數、個位數保存至sech、secl
		begin 
			sech=4'd0;
			secl=4'd0;
			for(i=5; i>=0; i=i-1)
				begin	
					if(sech>=5)
						sech=sech+3;
					if(secl>=5)
						secl=secl+3;
					sech = sech<<1;
					sech[0] = secl[3];
					secl = secl<<1;
					secl[0]= sec[i];
				end		
		end	
	
	always@(horl or horh) //將一條5位的horh/horl總線轉換成2條7位的led總線(供7位數碼管顯示)
		begin 
			case(horl)
				5'd0: led_hor_l<=7'b011_1111;
				5'd1: led_hor_l<=7'b000_0110;
				5'd2: led_hor_l<=7'b101_1011;
				5'd3: led_hor_l<=7'b100_1111;
				5'd4: led_hor_l<=7'b110_0110;
				5'd5: led_hor_l<=7'b110_1101;
				5'd6: led_hor_l<=7'b111_1100; 
				5'd7: led_hor_l<=7'b000_0111;
				5'd8: led_hor_l<=7'b111_1111;
				5'd9: led_hor_l<=7'b110_0111;
				default: led_hor_l<=7'b000_0000;
			endcase
			
			case(horh)      //高位數只有0,1,2三個值
				5'd0: led_hor_h<=7'b011_1111;
				5'd1: led_hor_h<=7'b000_0110;
				5'd2: led_hor_h<=7'b101_1011;
				default: led_hor_h<=7'b000_0000;
			endcase
		end	
		
		always@(minl or minh) //將一條6位的minh/minl總線轉換成2條7位的led總線(供7位數碼管顯示)
			begin 
				case(minl)		//低位數的可能值:0~9
					5'd0: led_min_l<=7'b011_1111;
					5'd1: led_min_l<=7'b000_0110;
					5'd2: led_min_l<=7'b101_1011;
					5'd3: led_min_l<=7'b100_1111;
					5'd4: led_min_l<=7'b110_0110;
					5'd5: led_min_l<=7'b110_1101;
					5'd6: led_min_l<=7'b111_1100; 
					5'd7: led_min_l<=7'b000_0111;
					5'd8: led_min_l<=7'b111_1111;
					5'd9: led_min_l<=7'b110_0111;
					default: led_min_l<=7'b000_0000;
				endcase
				
				case(minh)      //高位數的可能值只有:0,1,2,3,4,5
					5'd0: led_min_h<=7'b011_1111;
					5'd1: led_min_h<=7'b000_0110;
					5'd2: led_min_h<=7'b101_1011;
					5'd3: led_min_h<=7'b100_1111;
					5'd4: led_min_h<=7'b110_0110;
					5'd5: led_min_h<=7'b110_1101;
					default: led_min_h<=7'b000_0000;
				endcase
			end
			
		always@(secl or sech) //將一條6位的sech/sec總線轉換成2條7位的led總線(供7位數碼管顯示)
			begin 
				case(secl)		//低位數的可能值:0~9
					5'd0: led_sec_l<=7'b011_1111;
					5'd1: led_sec_l<=7'b000_0110;
					5'd2: led_sec_l<=7'b101_1011;
					5'd3: led_sec_l<=7'b100_1111;
					5'd4: led_sec_l<=7'b110_0110;
					5'd5: led_sec_l<=7'b110_1101;
					5'd6: led_sec_l<=7'b111_1100; 
					5'd7: led_sec_l<=7'b000_0111;
					5'd8: led_sec_l<=7'b111_1111;
					5'd9: led_sec_l<=7'b110_0111;
					default: led_sec_l<=7'b000_0000;
				endcase
				
				case(sech)      //高位數的可能值只有:0,1,2,3,4,5
					5'd0: led_sec_h<=7'b011_1111;
					5'd1: led_sec_h<=7'b000_0110;
					5'd2: led_sec_h<=7'b101_1011;
					5'd3: led_sec_h<=7'b100_1111;
					5'd4: led_sec_h<=7'b110_0110;
					5'd5: led_sec_h<=7'b110_1101;
					default: led_sec_h<=7'b000_0000;
				endcase
			end
	
endmodule
  • tb_led_dis.v(用於測試)

`timescale 1ms/1ps

module tb_led_dis();
	reg [4:0] HOR;
	reg [5:0] MIN, SEC;
	
	led_dis T3(.hor(HOR),.min(MIN), .sec(SEC));
	
	parameter delay = 1000;
	
	initial 
		begin
			#delay	HOR=5'd1; MIN=6'd12; SEC=6'd35;
			#delay	HOR=5'd11; MIN=6'd59; SEC=6'd12;
			#delay	HOR=5'd00; MIN=6'd34; SEC=6'd56;
			#delay	HOR=5'd23; MIN=6'd28; SEC=6'd00;
			#delay	;
		end
	
endmodule
LED顯示模塊—T3實驗結果

4、頂層模塊設計  (更新中)

  • 添加三個.v文件(引入子模塊,類似於C語言中的導入.h文件操作)

  • tb_top.v

module top(clk,
	horh,horl,minh,minl,sech,secl,
	led_hor_h, led_hor_l,
	led_min_h, led_min_l,
	led_sec_h, led_sec_l,
);

	input clk;

	output [3:0] horh, horl;
	output [3:0] minh, minl;
	output [3:0] sech, secl;
	output [6:0] led_hor_h, led_hor_l;
	output [6:0] led_min_h, led_min_l;
	output [6:0] led_sec_h, led_sec_l;

	wire clk_1;
	freq_div div_inst(.clk(clk),.clk_1(clk_1));

	wire [4:0] hor;
	wire [5:0] min,sec;
	count inst_count(.clk_1(clk_1),.hor(hor),.min(min),.sec(sec));

	wire [3:0] horh,horl;
	wire [3:0] minh,minl;
	wire [3:0] sech,secl;
	wire [6:0] led_hor_h, led_hor_l;
	wire [6:0] led_min_h, led_min_l;
	wire [6:0] led_sec_h, led_sec_l;	

	led_dis inst_led(.hor(hor),.min(min),.sec(sec), 
	.horh(horh),.horl(horl),
	.minh(minh),.minl(minl),
	.sech(sech),.secl(secl),
	.led_hor_l(led_hor_l),
	.led_hor_h(led_hor_h),
	.led_min_h(led_min_h),
	.led_min_l(led_min_l),
	.led_sec_h(led_sec_h),
	.led_sec_l(led_sec_l)
	);

endmodule

 


PS:直接編寫一個tb_led_clock.v就可以了,不用指定其頂層還是底層模塊

  • tb_led_clock.v

`timescale 1ns/1ps     

module tb_led_clock();
  reg CLK;
  parameter period=20;   //週期爲20*1ns= 1/50MHz
  top inst_top(.clk(CLK));
 
  initial 
    begin
      CLK= 0;
      forever 
        #(period/2) CLK = ~CLK; 
    end
endmodule

 

  • 拓展功能——復位、調時

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章