Verilog实现毫秒级计时器(秒表)
功能描述
使用Verilog语言在Nexys3开发板上实现一个毫秒精度的计时器。
计时器从0.000s开始计时到9.999s,然后重头开始计时,如此往复。Nexys3开发板上7段译码管要实时显示当前计数时间值和小数点。另外,跟一般的秒表类似,本文实现的计时器有4个控制按钮,分别为reset,start,stop以及increment,唯一不太一样的是increment按钮,在计时器停止的状态下按一下increment按钮,计时器的时间值会增加0.001s。
功能实现
计时器由4个不同的功能模块组成:
clk_divider: 时钟分频器,将Nexys3开发板的100MHz系统时钟转换为秒表的1KHz时钟;
timer_fsm: 计时器状态机,响应控制按钮的输入,跳转到不同状态(start, stop, inc, trap);
counter: 计时器,由4个0-9的计数器组成;
seg7_controller: 7段译码管控制器,以扫描显示的方式显示4位不同数字。
因此,只需要在顶级逻辑调用这4个模块便能构造出一个毫秒级的计时器。
计时器顶级逻辑图

时钟分频器电路图

计时器状态机图示

4 位十进制计数器电路图

7 段数码管控制器电路图
代码实现
// millitimer.v
`timescale 1ns / 1ps
module millitimer(
input start_btn,
input stop_btn,
input inc_btn,
input clk,
input rst,
output [3:0] an,
output [6:0] seg,
output dp
);
parameter DP_SELECT = 3'b111;
wire mclk;
wire cen;
wire [15:0] data;
timer_fsm u_tfs (
.start(start_btn),
.stop(stop_btn),
.inc(inc_btn),
.clk(mclk),
.rst(rst),
.cen(cen)
);
clk_divider u_clk_d (
.clk(clk),
.rst(rst),
.mclk(mclk)
);
counter u_count (
.cen(cen),
.clk(mclk),
.rst(rst),
.out(data)
);
seg7_controller u_seg7_ctrl (
.en(1'b1),
.data(data),
.dp_select(DP_SELECT),
.clk(mclk),
.rst(rst),
.an(an),
.seg(seg),
.dp(dp)
);
endmodule
// clk_divider.v
`timescale 1ns / 1ps
module clk_divider(
input clk,
input rst,
output reg mclk
);
parameter CLK_COUNT = 4; // 板级验证的时候该值改为49999
reg [31:0] count;
always @ (posedge clk or posedge rst)
begin
if (rst) begin
count <= 0;
mclk <= 0;
end
else if (count == CLK_COUNT) begin
count <= 0;
mclk <= ~mclk;
end
else begin
count <= count+1;
mclk <= mclk;
end
end
endmodule
// timer_fsm.v
`timescale 1ns / 1ps
module timer_fsm(
input start,
input stop,
input inc,
input clk,
input rst,
output reg cen
);
parameter STOP = 2'b00;
parameter START = 2'b01;
parameter INC = 2'b10;
parameter TRAP = 2'b11;
reg [1:0] state, nextstate;
always @(posedge clk or posedge rst)
begin
if (rst)
state <= STOP;
else
state <= nextstate;
end
always @(*)
begin
case (state)
STOP:
if (stop) nextstate = STOP;
else if (start) nextstate = START;
else if (inc) nextstate = INC;
else nextstate = STOP;
START:
if (start) nextstate = START;
else if (stop) nextstate = STOP;
else nextstate = START;
INC:
nextstate = TRAP;
TRAP:
if (inc) nextstate = TRAP;
else nextstate = STOP;
endcase
end
always @(*)
begin
case (state)
STOP: cen = 1'b0;
START: cen = 1'b1;
INC: cen = 1'b1;
TRAP: cen = 1'b0;
endcase
end
endmodule
// counter.v
`timescale 1ns / 1ps
module counter(
input cen,
input clk,
input rst,
output [15:0] out
);
wire tc0, tc1, tc2;
counter4b u_c41 (
.cen(cen),
.clk(clk),
.rst(rst),
.out(out[3:0]),
.tc(tc0)
);
counter4b u_c42 (
.cen(cen & tc0),
.clk(clk),
.rst(rst),
.out(out[7:4]),
.tc(tc1)
);
counter4b u_c43 (
.cen(cen & tc0 & tc1),
.clk(clk),
.rst(rst),
.out(out[11:8]),
.tc(tc2)
);
counter4b u_c44 (
.cen(cen & tc0 & tc1 & tc2),
.clk(clk),
.rst(rst),
.out(out[15:12]),
.tc()
);
endmodule
module counter4b (
input cen,
input clk,
input rst,
output reg [3:0] out,
output tc
);
assign tc = (out == 4'b1001) ? 1'b1 : 1'b0;
always @ (posedge clk or posedge rst)
begin
if (rst) begin
out <= 0;
end
else if (cen) begin
if (out == 4'b1001)
out <= 0;
else
out <= out+1;
end
else begin
out <= out;
end
end
endmodule
// seg7_controller.v
`timescale 1ns / 1ps
module seg7_controller(
input en,
input [15:0] data,
input [2:0] dp_select,
input clk,
input rst,
output reg [3:0] an,
output reg[6:0] seg,
output dp
);
reg [1:0] count;
reg [3:0] selected_data;
assign dp = (dp_select[2] && (dp_select[1:0]==count)) ? 1'b0 : 1'b1;
always @ (posedge clk or posedge rst)
begin
if (rst)
count <= 0;
else if (!en)
count <= 0;
else if (count == 2'b11)
count <= 0;
else
count <= count+1;
end
always @ (*)
begin
if (en) begin
case (count)
2'b00: an = 4'b1110;
2'b01: an = 4'b1101;
2'b10: an = 4'b1011;
2'b11: an = 4'b0111;
default: an = 4'b1111;
endcase
end
else begin
an = 4'b1111;
end
end
always @ (*)
begin
case (count)
2'b00: selected_data = data[3:0];
2'b01: selected_data = data[7:4];
2'b10: selected_data = data[11:8];
2'b11: selected_data = data[15:12];
default: selected_data = 4'b0000;
endcase
end
always @ (*)
begin
case (selected_data)
4'b0000: seg = 7'b0000001;
4'b0001: seg = 7'b1001111;
4'b0010: seg = 7'b0010010;
4'b0011: seg = 7'b0000110;
4'b0100: seg = 7'b1001100;
4'b0101: seg = 7'b0100100;
4'b0110: seg = 7'b0100000;
4'b0111: seg = 7'b0001111;
4'b1000: seg = 7'b0000000;
4'b1001: seg = 7'b0000100;
default: seg = 7'b1111111;
endcase
end
endmodule
// 仿真测试文件:millitimer_test.v
`timescale 1ns / 1ps
module millitimer_test;
// Inputs
reg start_btn;
reg stop_btn;
reg inc_btn;
reg clk;
reg rst;
// Outputs
wire [3:0] an;
wire [6:0] seg;
wire dp;
// Instantiate the Unit Under Test (UUT)
millitimer uut (
.start_btn(start_btn),
.stop_btn(stop_btn),
.inc_btn(inc_btn),
.clk(clk),
.rst(rst),
.an(an),
.seg(seg),
.dp(dp)
);
initial begin
// Initialize Inputs
start_btn = 0;
stop_btn = 0;
inc_btn = 0;
clk = 0;
rst = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
rst = 1;
#100;
rst = 0;
#100;
start_btn = 1;
#1200;
start_btn = 0;
stop_btn = 1;
#200;
inc_btn = 1;
#200;
stop_btn = 0;
#200;
start_btn = 1;
#200;
inc_btn = 0;
#200;
rst = 1;
#200;
rst = 0;
end
always #5
clk = ~clk;
endmodule
注意:附件有更加详细的文档描述和测试代码!
本文介绍如何使用Verilog在Nexys3开发板上实现毫秒精度的计时器,具备启动、暂停、重置及增量功能。计时器采用四个主要模块:时钟分频器、状态机、计数器和7段译码管控制器。
927





