본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 49일차 - Verilog(Stop Watch - Minute, Start/Stop, Lap)

by Graffitio 2023. 9. 4.
[Harman 세미콘 아카데미] 49일차 - Verilog(Stop Watch - Minute, Start/Stop, Lap)
728x90
반응형
[Stop Watch]

 

📌 Minute count 구현

 

Minute clock 설계

    clk_sec을 60번 카운트하면 1분

 

// min clock
module clock_min(
    input clk, clk_sec, reset_p,
    output clk_min
    );

    // 60개 카운트하면 1s
    reg [5:0] cnt_sec;
    reg cp_min; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_sec = 0; // reset이 들어오면 cnt = 0
        else if(clk_sec) begin
            if (cnt_sec >= 29) begin
                cnt_sec = 0; // 0부터 번까지 카운트하면 다시 0으로
                cp_min = ~cp_min;
            end
            else cnt_sec = cnt_sec + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_min), .rst(reset_p), .n_edge(clk_min)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule

 

Stop Watch에 Instance

    1. clock_min 모듈 Instance

    2. FND의 남은 상위 두 자리에 counter_dec_60 Instance

 

module Stop_Watch_top(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7
    );
    
    wire up_down_p;
    reg start_stop;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // 바운싱 제거용 DFF
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop)); //// 위의 clk divider의 16번쨰 bit를 써야 된다.
        
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    clock_usec usec_clk(.clk(clk), .reset_p(reset_p), .clk_usec(clk_usec));
    clock_msec msec_clk(.clk(clk), .clk_usec(clk_usec), .reset_p(reset_p), .clk_msec(clk_msec));
    clock_sec sec_clk(.clk(clk), .clk_msec(clk_msec), .reset_p(reset_p), .clk_sec(clk_sec)); // 3가지 다 불러와야 sec 분주기 사용 가능
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min));
    
    // 카운터 인스턴스
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60 cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10));
    
    wire [3:0] min1, min10;
    counter_dec_60 cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10));
    
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value({min10, min1, sec10, sec1}), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
endmodule


 

📌 Start/Stop 버튼 구현

 

Start/Stop 버튼 모듈 생성

    버튼을 누를 때마다 Start/Stop되도록 설계

 

module start_stop(
    input clk, reset_p,
    input start_stop_input,
    input btn,
    output reg start_stop
);
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            start_stop <= 0; // 리셋 시에 초기화
        end
        else begin
            // 버튼 누를 때마다 상태 토글
            if (btn && !start_stop_input) begin
                start_stop <= ~start_stop;
            end
        end
    end
endmodule

 

enable 기능 추가

    이전 clock_usec 모듈에 enable 기능 추가

 

// Cora board sys clock : 125MHz
// 1 clock 당, 8ns

// Micro sec clock
module clock_usec(
    input clk, reset_p,
    input enable,
    output clk_usec
    );
    
    // 125개 카운트하면 1us
    reg [6:0] cnt_8nsec;
    wire cp_usec; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
        else if (!enable) begin
            if(cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
            else cnt_8nsec = cnt_8nsec + 1;
        end
    end
    
    assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1 
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(reset_p), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule

 

Stop Watch Top 모듈에 Instance

   btn을 누르면, start/stop 신호가 토글되고 해당 신호를 clock의 enable에 연결하여

   clock 활성화/비활성화 기능을 하도록 설계

 

module Stop_Watch_top(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7
    );
    
    wire up_down_p, start_stop_input;
    reg start_stop;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // 바운싱 제거용 DFF
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop_input)); //// 위의 clk divider의 16번쨰 bit를 써야 된다.
    
    // start_stop 버튼
    start_stop srt_stp(.clk(clk), .reset_p(reset_p), .start_stop_input(start_stop_input), .btn(btn[0]), .start_stop(start_stop));
    
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    clock_usec usec_clk(.clk(clk), .reset_p(reset_p), .enable(start_stop), .clk_usec(clk_usec));
    clock_msec msec_clk(.clk(clk), .clk_usec(clk_usec), .reset_p(reset_p), .clk_msec(clk_msec));
    clock_sec sec_clk(.clk(clk), .clk_msec(clk_msec), .reset_p(reset_p), .clk_sec(clk_sec)); // 3가지 다 불러와야 sec 분주기 사용 가능
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min));
    
    // 카운터 인스턴스
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60 cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10));
    
    wire [3:0] min1, min10;
    counter_dec_60 cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10));
    
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value({min10, min1, sec10, sec1}), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
endmodule

 


 

📌 Lap 기능 구현

 

Lap 버튼 구현

    T Flip-Flop를 활용하여 버튼의 토글 기능 구현(버튼 하나로 On/Off 가능)

    DFF로 디바운싱, Edge Detector로 채터링 제거

 

// Lap 기능 구현
    wire lap_input, lap_btn, lap;
    D_flip_flop_n debnc_lap(.d(btn[1]), .clk(clk_div[16]), .rst(reset_p), .q(lap_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_lap(.clk(clk), .cp_in(lap_input), .rst(reset_p), .n_edge(lap_btn)); // Edga Detector
    t_flip_flop_p tff_lap(.clk(clk), .rst(reset_p), .t(lap_btn), .q(lap)); // 토글을 위한 TFF

 

Lap data 저장할 레지스터 설계

 

// lap 데이터 저장을 위한 레지스터 생성
    reg [15:0] lap_value;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {min10, min1, sec10, sec1};
    end

 

Lap Data Select 

    lap = 1 : lap_value 출력

    lap = 0 : 현재 count 출력

 

    // lap 버튼에 따른 FND 출력
    wire [15:0] value;
    assign value = lap ? lap_value : {min10, min1, sec10, sec1};

 

All Code(StopWatch)

 

module Stop_Watch_top(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7
    );
    
    wire start_stop_input, start_stop, start_stop_btn;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // Start_stop 기능 구현
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_start_stop(.clk(clk), .cp_in(start_stop_input), .rst(reset_p), .n_edge(start_stop_btn)); // Edga Detector
    start_stop_btn srt_stp(.clk(clk), .reset_p(reset_p), .start_stop_input(start_stop_btn), .btn(btn[0]), .start_stop(start_stop)); // start_stop 버튼 토글 기능
    
    // Lap 기능 구현
    wire lap_input, lap_btn, lap;
    D_flip_flop_n debnc_lap(.d(btn[1]), .clk(clk_div[16]), .rst(reset_p), .q(lap_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_lap(.clk(clk), .cp_in(lap_input), .rst(reset_p), .n_edge(lap_btn)); // Edga Detector
    t_flip_flop_p tff_lap(.clk(clk), .rst(reset_p), .t(lap_btn), .q(lap)); // 토글을 위한 TFF
    
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    clock_usec usec_clk(.clk(clk), .reset_p(reset_p), .enable(start_stop), .clk_usec(clk_usec));
    clock_msec msec_clk(.clk(clk), .clk_usec(clk_usec), .reset_p(reset_p), .clk_msec(clk_msec));
    clock_sec sec_clk(.clk(clk), .clk_msec(clk_msec), .reset_p(reset_p), .clk_sec(clk_sec)); // 3가지 다 불러와야 sec 분주기 사용 가능
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min));
    
    // 카운터 인스턴스
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60 cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10)); // sec count
    wire [3:0] min1, min10; // min1 : 1의자리, min10 : 10의 자리
    counter_dec_60 cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10)); // min count
    
    // lap 데이터 저장을 위한 레지스터 생성
    reg [15:0] lap_value;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {min10, min1, sec10, sec1};
    end
    
    // lap 버튼에 따른 FND 출력
    wire [15:0] value;
    assign value = lap ? lap_value : {min10, min1, sec10, sec1};
    
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value(value), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
endmodule

 


 

[StopWatch All]

 

 

StopWatch_top

더보기
module Stop_Watch_top(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7
    );
    
    wire start_stop_input, start_stop, start_stop_btn;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // Start_stop 기능 구현
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_start_stop(.clk(clk), .cp_in(start_stop_input), .rst(reset_p), .n_edge(start_stop_btn)); // Edga Detector
    start_stop_btn srt_stp(.clk(clk), .reset_p(reset_p), .start_stop_input(start_stop_btn), .btn(btn[0]), .start_stop(start_stop)); // start_stop 버튼 토글 기능
    
    // Lap 기능 구현
    wire lap_input, lap_btn, lap;
    D_flip_flop_n debnc_lap(.d(btn[1]), .clk(clk_div[16]), .rst(reset_p), .q(lap_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_lap(.clk(clk), .cp_in(lap_input), .rst(reset_p), .n_edge(lap_btn)); // Edga Detector
    t_flip_flop_p tff_lap(.clk(clk), .rst(reset_p), .t(lap_btn), .q(lap)); // 토글을 위한 TFF
    
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    clock_usec usec_clk(.clk(clk), .reset_p(reset_p), .enable(start_stop), .clk_usec(clk_usec));
    clock_msec msec_clk(.clk(clk), .clk_usec(clk_usec), .reset_p(reset_p), .clk_msec(clk_msec));
    clock_sec sec_clk(.clk(clk), .clk_msec(clk_msec), .reset_p(reset_p), .clk_sec(clk_sec)); // 3가지 다 불러와야 sec 분주기 사용 가능
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min));
    
    // 카운터 인스턴스
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60 cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10)); // sec count
    wire [3:0] min1, min10; // min1 : 1의자리, min10 : 10의 자리
    counter_dec_60 cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10)); // min count
    
    // Lap Value / value Select
    reg [15:0] lap_value;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {min10, min1, sec10, sec1};
    end
    
    // lap 버튼에 따른 FND 출력
    wire [15:0] value;
    assign value = lap ? lap_value : {min10, min1, sec10, sec1};
    
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value(value), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
endmodule


// 60bit_Counter
module counter_dec_60(
    input clk, reset_p,
    input clk_time,
    output reg [3:0] dec1, dec10
    );
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) begin
            dec1 = 0;
            dec10 = 0;
        end
        else if(clk_time) begin
            if(dec1 >= 9) begin 
                dec1 <= 0;
                if(dec10 >= 5) dec10 = 0;
                else dec10 <= dec10 + 1;
            end
            else dec1 <= dec1 + 1;
        end
    end
endmodule


// Start/Stop 버튼
module start_stop_btn(
    input clk, reset_p,
    input start_stop_input,
    input btn,
    output reg start_stop
);
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            start_stop <= 0; // 리셋 시에 초기화
        end
        else begin
            // 버튼 누를 때마다 상태 토글
            if (btn && !start_stop_input) begin
                start_stop <= ~start_stop;
            end
        end
    end
endmodule

 

Clock_Library

더보기
// Cora board sys clock : 125MHz
// 1 clock 당, 8ns

// Micro sec clock
module clock_usec(
    input clk, reset_p,
    input enable,
    output clk_usec
    );
    
    // 125개 카운트하면 1us
    reg [6:0] cnt_8nsec;
    wire cp_usec; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
        else if (!enable) begin
            if(cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
            else cnt_8nsec = cnt_8nsec + 1;
        end
    end
    
    assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1 
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(reset_p), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule


// Mili sec clock
module clock_msec(
    input clk, clk_usec, reset_p,
    output clk_msec
    );
    
    // 1000개 카운트하면 1ms
//    reg [9:0] cnt_usec;
//    wire cp_msec; // cp : clock pulse
    
//    always @(posedge clk, posedge reset_p) begin
//        if(reset_p) cnt_usec = 0; // reset이 들어오면 cnt = 0
//        else if (cnt_usec >= 999) cnt_usec = 0; // 0부터 125번까지 카운트하면 다시 0으로
//        else cnt_usec = cnt_usec + 1;
//    end
    
//    assign cp_msec = cnt_usec < 499 ? 0 : 1; // 0~498 : 0, 499~1000 : 1 

    // 1000개 카운트하면 1ms
    reg [8:0] cnt_usec;
    reg cp_msec; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_usec = 0; // reset이 들어오면 cnt = 0
        else if(clk_usec) begin
            if (cnt_usec >= 499) begin
                cnt_usec = 0; // 0부터 499번까지 카운트하면 다시 0으로
                cp_msec = ~cp_msec;
            end
            else cnt_usec = cnt_usec + 1;
        end

    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_msec), .rst(reset_p), .n_edge(clk_msec)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule



// sec clock
module clock_sec(
    input clk, clk_msec, reset_p,
    output clk_sec
    );

    // 1000개 카운트하면 1s
    reg [8:0] cnt_msec;
    reg cp_sec; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_msec = 0; // reset이 들어오면 cnt = 0
        else if(clk_msec) begin 
            if (cnt_msec >= 499) begin
                cnt_msec = 0; // 0부터 499번까지 카운트하면 다시 0으로
                cp_sec = ~cp_sec; // clock pulse 반전
            end
            else cnt_msec = cnt_msec + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_sec), .rst(reset_p), .n_edge(clk_sec)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule



// min clock
module clock_min(
    input clk, clk_sec, reset_p,
    output clk_min
    );

    // 60개 카운트하면 1s
    reg [5:0] cnt_sec;
    reg cp_min; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_sec = 0; // reset이 들어오면 cnt = 0
        else if(clk_sec) begin
            if (cnt_sec >= 29) begin
                cnt_sec = 0; // 0부터 번까지 카운트하면 다시 0으로
                cp_min = ~cp_min;
            end
            else cnt_sec = cnt_sec + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_min), .rst(reset_p), .n_edge(clk_min)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule

 

FND_Controller

더보기
module FND_4digit_cntr( //// ring counter top module 생성 ---- 16bit의 값을 받아서 1ms 간격으로 한 자리씩 켜준다.
    input clk, rst, lap,
    input [15:0] value, //// 4자리니까 16bit
    output [3:0] com,
    output [7:0] seg_7
);
    reg [16:0] clk_1ms;//// 8ns * 2^17 = 1,048,576ns = 1048㎲ = 1.048ms
    always @(posedge clk) clk_1ms = clk_1ms + 1 ;
    
    ring_count_fnd ring1(.clk(clk_1ms[16]), .com(com)); //// 1ms posedge마다 좌시프트
                                                                   //// 1ms 마다 CT 순대로 LED가 점멸한다.
                                                                   
    wire [7:0] seg_7_font;
    reg [3:0] hex_value;
    decoder_7seg ring_seg_7(.hex_value(hex_value), .seg_7(seg_7_font));
    assign seg_7 = ~seg_7_font;
    
    always @(negedge clk) begin //// 카운팅은 posedge에 했으니, 겹치지 않게 negedge
        case(com)
            4'b1110 : hex_value = value[15:12]; //// CT1에 해당하는 hex_value는 value[15:12]
            4'b1101 : hex_value = value[11:8];
            4'b1011 : hex_value = value[7:4];
            4'b0111 : hex_value = value[3:0];
        endcase  //// edge triggering은 default에서 현재 값을 유지해도 FF가 만들어지니 상관없다.           
    end
endmodule

 


 

728x90
반응형