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个模块便能构造出一个毫秒级的计时器。
代码实现
// 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
注意:附件有更加详细的文档描述和测试代码!