EDA digital clock (2)


foreword

Rewrite the EDA digital clock , try to keep the original function unchanged, modify the relationship between modules and the internal logic of the module, so that the program is more concise and conforms to the circuit specification.


1. Design content

The design function and implementation method are consistent with the previous one: 1. It has the function of timing "second", "minute" and "hour", and the hour counter is
in 24-hour format;
"Hour" to adjust;
3. It has the function of manually inputting and setting the timing alarm clock, and it lights up for 1 minute;
4. The clock reset function can be realized: 00:00:00;

2. Module structure

There are two major changes:
(1) Change the n-ary counter module to a non-carry counter with synchronous zero setting;
(2) Add a debounce module to make the button input more stable.

3. Code writing

1. Top-level module Digclk

The priority of the buttons decreases from top to bottom
. Shift and increase are input through the debounce module.
The reset and count buttons are asynchronous. The
shift and increase buttons are used for timing and alarm clock data
input. /Alarm clock/Hour/Minute/Minute/Second/No display)
The priority of display dialing codes decreases from top to bottom (excluding timing enable dialing codes).
Since there are only four digital tubes, the hours, minutes/minutes and seconds are displayed separately. The
second counting is displayed by an LED
In addition, 2 LEDs are used to represent the hourly timekeeping and timing alarm clock

module Digclk(
    input clk,//20ns脉冲
    input rst_n,//复位按键BTN0
    input set,//置数按键BTN1
    input move,//移位按键BTN2
    input inc,//增加按键BTN3
    input set_en,//校时拨码SW7
    input alm_en,//闹钟拨码SW6
    input h_m_en,//时分显示拨码SW5
    input m_s_en,//分秒显示拨码SW4
    input cnt_en,//计时使能拨码SW3
    output [3:0] loc,//数码管位置
    output [7:0] pin,//数码管引脚电平
    output h_alm,//整点报时LD2
    output s_alm,//定时闹钟LD1
    output sec);//秒计时LD0

    //消抖部分
    wire move_f, inc_f;//消抖后的按钮信号
    Filter u0_filter(
        .clk(clk),
        .rst_n(rst_n),
        .botton(move),
        .botton_f(move_f));
    Filter u1_filter(
        .clk(clk),
        .rst_n(rst_n),
        .botton(inc),
        .botton_f(inc_f));//消抖模块

    //校时部分
    reg [5:0] set_n;//校时信号
    wire [15:0] set_num;//校时部分数据
    reg [23:0] set_time;//校时整体数据
    wire [3:0] set_loc;//指针位置
    Set_time u0_set_time(
        .rst_n(rst_n),
        .en(set_en),
        .move(move_f),
        .inc(inc_f),
        .set_num(set_num),
        .set_loc(set_loc));//移位输入模块
    always @(set, set_en, alm_en, h_m_en, m_s_en) begin//判断所需修改的位
        if(set) set_n = 6'b000000;
        else if(set_en || alm_en) set_n = 6'b000000;
        else if(h_m_en) set_n = 6'b111100;
        else if(m_s_en) set_n = 6'b001111;
        else set_n = 6'b000000;
    end
    always @(h_m_en, set_num) begin//用于和计时部分连接
        if(h_m_en)
            set_time = {
    
    set_num, 8'b00000000};
        else set_time = {
    
    8'b00000000, set_num};
    end

    //计时部分
    wire clk_cnt;//计时时钟
    wire [23:0] cnt_num;//计时数据
    wire [5:0] co;//进位信号
    Div_cnt u_div_cnt(
        .clk(clk),
        .rst_n(rst_n),
        .clk_cnt(clk_cnt));//分频模块
    Time_cnt u_time_cnt(
        .clk(clk_cnt),
        .rst_n(rst_n),
        .en(cnt_en),
        .set_n(set_n),
        .set_num(set_time),
        .cnt(cnt_num),
        .co(co));//计时模块
    assign sec = cnt_num % 2;

    //整点报时
    H_alarm u_h_alarm(
        .clk(clk_cnt),
        .rst_n(rst_n),
        .en(co[3] & co[2] & co[1] & co[0]),//同步进位使能需要将前级所有进位相与
        .num(cnt_num[23:16]),
        .led_out(h_alm));//整点报时模块

    //闹钟部分
    wire [15:0] alm_num;//闹钟数据
    wire [3:0] alm_loc;//指针位置
    Set_time u1_set_time(
        .rst_n(rst_n),
        .en(!set_en && alm_en),
        .move(move_f),
        .inc(inc_f),
        .set_num(alm_num),
        .set_loc(alm_loc));
    assign s_alm = (cnt_num[23:8] == alm_num) && cnt_en;//闹钟响一分钟

    //显示部分
    Scan u_scan(.clk(clk), .rst_n(rst_n), .loc(loc));//扫描模块
    reg [15:0] show_num;//待取数字
    reg [3:0] show_dot;//小数点代表指针位置
    always @(*) begin//取数(校时/闹钟/计时)
        if(set_en) begin
            show_num = set_num;
            show_dot = set_loc;
        end
        else if(alm_en) begin
            show_num = alm_num;
            show_dot = alm_loc;
        end
        else if(h_m_en) begin 
            show_num = cnt_num[23:8];
            show_dot = 4'b0000;
        end
        else if(m_s_en) begin
            show_num = cnt_num[15:0];
            show_dot = 4'b0000;
        end
        else begin
            show_num = 0;
            show_dot = 4'b0000;
        end            
    end
    reg [4:0] con_num;//待译码数字,最后一位为小数点
    always @(loc, show_num, show_dot) begin//取数(41case(loc)
            4'b0111: con_num = {
    
    show_num[15:12], show_dot[3]};
            4'b1011: con_num = {
    
    show_num[11:8], show_dot[2]};
            4'b1101: con_num = {
    
    show_num[7:4], show_dot[1]};
            4'b1110: con_num = {
    
    show_num[3:0], show_dot[0]};
            default: con_num = 5'bxxxxx;//避免锁存
        endcase
    end
    Convert u_convert(.num(con_num), .pin(pin));//译码模块
    
endmodule

2. Debounce Module Filter

Send a negative pulse of one clock cycle after pressing the button (lower edge) for a period of time. During
this period, no button signal is received.
Delay 500ms
counter_end = 50M * delay time / 2 - 1

module Filter(
    input clk,
    input rst_n,
    input botton,
    output botton_f);
    
    //parameter CNT_END = 12 499 999;//下载时延时500ms
    parameter CNT_END = 4;//仿真时延时100ns
    reg [23:0] counter;
    reg dly1, dly2;//延时标志位

    //dly1输出
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) dly1 <= 0;
        else if(!botton) dly1 <= 1;
        else if(counter >= CNT_END) dly1 <= 0;
    end

    //延时计数
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) counter <= 0;
        else if(dly1) counter <= counter + 1;
        else counter <= 0;
    end

    //dly2输出
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) dly2 <= 0;
        else dly2 <= dly1;
    end

    assign botton_f = dly1 || !dly2;//控制输出一个时钟的负脉冲

endmodule

3. Shift input module Set_time

The input content is stored by 4 non-carry counter sub-modules.
set_loc is used to record which bit counter
set_num is used to record the value of the time correction.
Every time the apply button is pressed, the pointer moves one bit to the right;
every time the inc button is pressed, the current The counter gets a cp, which is the value plus 1

module Set_time(
    input rst_n,
    input en,
    input move,
    input inc,
    output [15:0] set_num,
    output [3:0] set_loc);

    reg [3:0]loc;//指针位置
    //循环移位,时序逻辑
    always @(negedge rst_n, negedge move) begin
        if(!rst_n) loc <= 4'b1000;
        else if(!move && en) loc <= {
    
    loc[0], loc[3:1]};
    end
    assign set_loc = loc;

    //计数器子模块
    Count u0_count(
        .clk(inc),
        .rst_n(rst_n),
        .en(loc[0] && en),
        .set_0(set_num[3:0]==9),
        .set_n(1'b0),
        .set_num(4'b0000),
        .cnt(set_num[3:0]));
    Count u1_count(
        .clk(inc),
        .rst_n(rst_n),
        .en(loc[1] && en),
        .set_0(set_num[7:4]==9),
        .set_n(1'b0),
        .set_num(4'b0000),
        .cnt(set_num[7:4]));
    Count u2_count(
        .clk(inc),
        .rst_n(rst_n),
        .en(loc[2] && en),
        .set_0(set_num[11:8]==9),
        .set_n(1'b0),
        .set_num(4'b0000),
        .cnt(set_num[11:8]));
    Count u3_count(
        .clk(inc),
        .rst_n(rst_n),
        .en(loc[3] && en),
        .set_0(set_num[15:12]==9),
        .set_n(1'b0),
        .set_num(4'b0000),
        .cnt(set_num[15:12]));

endmodule

4. Counting module Count

Hexadecimal counter
Synchronous zero setting is completed in an external circuit
, including asynchronous number setting function

module Count(
    input clk,//时钟
    input rst_n,//异步复位
    input en,//使能
    input set_0,//同步置0
    input set_n,//异步置数
    input [3:0] set_num,//置数内容
    output reg [3:0] cnt);//计数值

    always @(posedge clk, negedge rst_n, posedge set_n) begin
        if(!rst_n) cnt <= 0;
        else if(set_n) cnt <= set_num;
        else if(set_0 && en) cnt <= 0;
        else if(en) cnt <= cnt + 1;
    end

endmodule

5. Counter frequency division module Div_cnt

When the system clock is 50MHz,
it outputs 1MHz when it is simulated.
When it is downloaded, it outputs 1Hz
rising edge to trigger counting, so as to ensure that the first count after reset
is CNT_END=50M/(2x counting frequency)

module Div_cnt (
    input clk,
    input rst_n,
    output reg clk_cnt);

    reg [24:0] counter;
    parameter [24:0] CNT_END = 24;//仿真时计数25
    //parameter [24:0] CNT_END = 24 999 999;//下载时计数25M

    always @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            counter <= 0;
            clk_cnt <= 1;//上升沿触发计数
        end
        else if (counter == CNT_END) begin
            counter <= 0;
            clk_cnt <= !clk_cnt;
        end
        else counter <= counter + 1;
    end

endmodule

6. Timing module Time_cnt

6 non-carry counters indicate hours, minutes and seconds
Carrying of counters is done through external connections
Contains asynchronous clearing and counting functions
In order to prevent the timing value from exceeding the base system, the carry signal is replaced by >= ==

module Time_cnt(
    input clk,
    input rst_n,
    input en,
    input [5:0] set_n,
    input [23:0] set_num,
    output [23:0] cnt,
    output [5:0] co);

    wire [5:0] enable;
    //计数器子模块
    //秒个位
    Count u0_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[0]),
        .set_0(co[0]),
        .set_n(set_n[0]),
        .set_num(set_num[3:0]),
        .cnt(cnt[3:0]));
    assign enable[0] = en;
    assign co[0] = (cnt[3:0] >= 9);
    //秒十位
    Count u1_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[1]),
        .set_0(co[1]),
        .set_n(set_n[1]),
        .set_num(set_num[7:4]),
        .cnt(cnt[7:4]));
    assign enable[1] = enable[0] && co[0];
    assign co[1] = (cnt[7:4] >= 5);
    //分个位
    Count u2_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[2]),
        .set_0(co[2]),
        .set_n(set_n[2]),
        .set_num(set_num[11:8]),
        .cnt(cnt[11:8]));
    assign enable[2] = enable[1] && co[1];
    assign co[2] = (cnt[11:8] >= 9);
    //分十位
    Count u3_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[3]),
        .set_0(co[3]),
        .set_n(set_n[3]),
        .set_num(set_num[15:12]),
        .cnt(cnt[15:12]));
    assign enable[3] = enable[2] && co[2];
    assign co[3] = (cnt[15:12] >= 5);
    //时个位
    Count u4_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[4]),
        .set_0(co[4]),
        .set_n(set_n[4]),
        .set_num(set_num[19:16]),
        .cnt(cnt[19:16]));
    assign enable[4] = enable[3] && co[3];
    assign co[4] = (cnt[19:16] >= 9) || co[5];
    //时十位
    Count u5_count(
        .clk(clk),
        .rst_n(rst_n),
        .en(enable[5]),
        .set_0(co[5]),
        .set_n(set_n[5]),
        .set_num(set_num[23:20]),
        .cnt(cnt[23:20]));
    assign enable[5] = enable[4] && co[4];
    assign co[5] = (cnt[23:20] >= 2) && (cnt[19:16] >= 3);

endmodule

7. Hourly time reporting module H_alarm

The LED flashes a few times at
0 o'clock and 24 times at 0 o'clock
. Common anode LEDs are lit at low levels
. Refer to the debounce module. At 24 o'clock, the flag is pulled low after the 47th time, and the counter counts to 48.
cnt_end = 2 * (num[7: 4] * 10 + num[3:0] + 1) - 1 - 1;

module H_alarm(
    input clk,
    input rst_n,
    input en,
    input [7:0] num,
    output led_out);

    reg [5:0] counter;//最多计到47
    reg [5:0] cnt_end;//将BCD码转为十进制
    reg flag;//运行标志位

    always @(posedge clk, negedge rst_n) begin//参考消抖模块
        if(!rst_n) begin
            flag <= 0;
            cnt_end <= 0;
        end
        else if(en) begin
            flag <= 1;
            cnt_end <= 2 * (num[7:4] * 10 + num[3:0]);
        end
        else if(counter >= cnt_end) begin
            flag <= 0;
            cnt_end <= 0;
        end
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) counter <= 0;
        else if(flag) counter <= counter + 1;
        else counter <= 0;
    end

    assign led_out = counter % 2;

endmodule

8. Scan module Scan

Scanning frequency division, system clock 50MHz
, 2.5MHz for simulation (5 rounds of scanning for every two counts),
100Hz for downloading (100Hz for one round of scanning),
digital tube selection, 0 means selected
Level trigger scanning
CNT_END=50M/(scanning number x scan frequency)-1

module Scan(
    input clk,
    input rst_n,
    output reg [3:0] loc);
    //扫描分频
    reg [16:0] counter = 0;//最多计到124 999
    reg [1:0] flag = 0;//选择标志
    parameter [16:0] CNT_END = 4;//仿真时计数5
    //parameter [16:0] CNT_END = 124 999;//下载时计数125 000

    always @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            counter <= 0;
            flag <= 0;
        end
        if (counter == CNT_END) begin
            counter <= 0;
            flag <= flag + 1;
        end
        else counter <= counter + 1;
    end
    //数码管选择
    always @(flag) begin
        case(flag)
            0: loc = 4'b1110;
            1: loc = 4'b1101;
            2: loc = 4'b1011;
            3: loc = 4'b0111;
            default loc = 4'bxxxx;
        endcase
    end

endmodule

9. Decoding module Convert

Convert the obtained number to the level corresponding to the digital tube
Combination logic
Low level active

module Convert(
    input [4:0] num,
    output reg [7:0] pin);

    always @(num) begin
	    case(num[4:1])
			0: pin[7:1] = 8'b0000001;
			1: pin[7:1] = 8'b1001111;
			2: pin[7:1] = 8'b0010010;
			3: pin[7:1] = 8'b0000110;
			4: pin[7:1] = 8'b1001100;
			5: pin[7:1] = 8'b0100100;
			6: pin[7:1] = 8'b0100000;
			7: pin[7:1] = 8'b0001111;
			8: pin[7:1] = 8'b0000000;
			9: pin[7:1] = 8'b0000100;
			default: pin = 8'b0110000;//其他状态显示E
        endcase
        pin[0] = ~num[0];//小数点
	end

endmodule

4. Test file

Simulate keys by combining function and repeat, which simplifies the application of incentives.

`timescale 1ns/1ns
module Test_Top;
	// Inputs
	reg clk;
	reg rst_n;
	reg set;
	reg move;
	reg inc;
	reg set_en;
	reg alm_en;
	reg h_m_en;
	reg m_s_en;
	reg cnt_en;

	// Outputs
	wire [3:0] loc;
	wire [7:0] pin;
	wire h_alm;
	wire s_alm;
	wire sec;

    //模拟按键按下
    task SET;
        begin
	        #200 set = 0;
            #50 set = 1;
        end
    endtask
    task MOVE;
        begin
	        #200 move = 0;
            #50 move = 1;
        end
    endtask
    task INC;
        begin
	        #200 inc = 0;
            #50 inc = 1;
        end
    endtask

	// Instantiate the Unit Under Test (UUT)
	Digclk uut (
		.clk(clk), 
		.rst_n(rst_n), 	
		.set(set), 
		.move(move), 
		.inc(inc), 
		.set_en(set_en), 
		.alm_en(alm_en), 
		.h_m_en(h_m_en), 
		.m_s_en(m_s_en),
		.cnt_en(cnt_en),
		.loc(loc),
		.pin(pin),
		.h_alm(h_alm),
		.s_alm(s_alm),
		.sec(sec)
	);
	
	//时钟周期20ns
	parameter PERIOD = 20;
	
	//时钟信号
    initial clk = 0;
	always #(PERIOD/2) clk = ~clk;
    
    //初始化
	initial begin
		// Initialize Inputs
		rst_n = 1;
		set = 1;
		move = 1;
		inc = 1;
		set_en = 0;
		alm_en = 0;
		h_m_en = 0;
		m_s_en = 0;
		cnt_en = 0;
		
		// Wait 200 ns for global rst_n to finish
		#100 rst_n = 0;
        #100 rst_n = 1;
		
		// Add stimulus here
//////////////////////////////////////////////使能拨码测试
		//开始计数
		#100 cnt_en = 1;
		//显示分秒
		#1000 m_s_en = 1;
		//显示时分
		#1000 h_m_en = 1;
		//闹钟模式
		#100 alm_en = 1;
//////////////////////////////////////////////闹钟测试
		//闹钟置数1200,闹钟指针1000
		INC;//1
		MOVE;
		repeat(2) INC;//2
//////////////////////////////////////////////校时测试
		//时分置数1100,校时指针1000
        //置数模式
		#100 set_en = 1;
        INC;//1
        MOVE;
        INC;//1
		#100 set_en = 0;
        #100 alm_en = 0;
		SET;
		//分秒置数5949,校时指针0100
		#100 set_en = 1;
		repeat(3) MOVE;
		repeat(4) INC;//5
		MOVE;
		repeat(8) INC;//9
		MOVE;
		repeat(4) INC;//4
		MOVE;			
		repeat(9) INC;//9
		#100 set_en = 0;
        #100 h_m_en = 0;
		SET;
//////////////////////////////////////////////计时使能拨码测试
		//停止计时
		#100000 cnt_en = 0;
//////////////////////////////////////////////清零测试
		//清零
		#1000 rst_n = 0;
        #100 rst_n = 1;
//////////////////////////////////////////////进位测试
		//分秒置数5949,校时指针1000
        #100 set_en = 1;
		repeat(5) INC;//5
		MOVE;
		repeat(9) INC;//9
		MOVE;
		repeat(4) INC;//4
		MOVE;			
		repeat(9) INC;//9
        #100 set_en = 0;
		SET;
		//时分置数2359,校时指针0001
		#100 set_en = 1;
        MOVE;
		repeat(7) INC;//2
		MOVE;
		repeat(4) INC;//3
		MOVE;
		INC;//5
		#100 set_en = 0;
        #100 m_s_en = 0;
		INC;//空加(当set_en为0时,inc按键不应起作用)
        SET;//空置数(当h_m_en与m_s_en为0时,set按键不应起作用)
		#100 h_m_en = 1;
        SET;//时分置数
		//恢复计时
		#100 cnt_en = 1;
//////////////////////////////////////////////防抖测试
        #100 set_en = 1;//校时指针0010
        #100 inc = 0;//触发
        #10 inc = 1;
        #30 inc = 0;//忽略
        #50 inc = 1;
        #70 inc = 0;//触发
        #90 inc = 1;
	end

endmodule

5. Waveform Simulation

The variable name and test content are basically similar to the previous one, so there is no need to describe too much.
1. Explanation of variable names
The variable names are different, but the positions remain completely the same.

2. Enable DIP test
For detailed explanation, see the previous article, the same below.

3. Alarm clock setting test
Here, the alarm clock is set directly without SET, which is different from the previous article (because I forgot the original logic when I rewrote it).

4. Clock setting test
Note: The FPGA button is active at low level (although I don't know why the original program can also be used).

5. Zero clear test

6. Timing enable dial test

7. Carry test

8. Anti-shake test


Summarize

Part of the variable names have been modified, but the input and output of the top-level module remain basically unchanged. Since the program only performs a simple simulation, it is not ruled out that there may be problems in actual use.
I might be just beginning,
I might be near the end.

Guess you like

Origin blog.csdn.net/qq_53715621/article/details/127589175