文章目錄
設計方案概述
本任務設計了一個簡易數字鐘,能實現小時、分鐘和秒的計時及顯示,其中,通過控制時、分和秒實現時鐘計時的計數模塊是本次設計的核心。
計數模塊的關鍵在於能夠理解三個計時單位之間的聯繫,即秒計數滿60產生一個向分鐘的進位,分鐘計數滿60產生一個向小時的進位,這兩個進位信號將小時、分和秒聯繫起來,是理解本設計的關鍵點。
爲時鐘設計一個初值設置控制信號,按下設置信號時能利用開發板上的撥碼開關或按鍵對時間進行校對設置。
-
任務分析
計數信號來自於開發板提供的時鐘信號,這裏採用的晶振頻率爲50MHz,而秒的計數週期是1秒,因此需要對該時鐘信號進行分頻,因此,分頻器模塊是本任務中另一個重要模塊。
根據常識,“秒”、“分”和“小時”之間存在各自獨立又互相聯繫的計數和進位關係,“秒”和“分”的計數模式相同,每計數滿60個時鐘清零並重新開始計數,相當於一個六十進制的計數器。“小時”的計數模式是每計數滿24個時鐘就進行清零並重新開始計數,相當於一個二十四進制的計數器。所以,六十進制和二十四進制的計數器模塊設計是本任務的核心模塊。
“秒”、“分”和“小時”的計時過程都需要通過LED顯示器進行顯示,所以還需要設計顯示控制模塊。
根據以上分析,本任務需要設計的模塊如下:
-
分頻器模塊;
-
分、秒和小時的計數器模塊;
-
顯示控制模塊。
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
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
3、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
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
-
拓展功能——復位、調時