카테고리 없음

Verilog 스톱워치

오버헤드프레스 2023. 4. 25. 11:21
`timescale 1ns / 1ps

module watch_top(
    input clk,
    input [1:0] btn,
    output [3:0] com_an,
    output [6:0] seg
    );
    
    
//    wire clk_sec;
    reg [3:0] sec [1:0];
    reg [3:0] min [1:0];
    
    wire [1:0] debounced_btn;
    
    clock_usec uCLK (.clk(clk), .clk_usec(clk_usec));
    clock_msec mCLK (.clk_usec(clk_usec), .clk_msec(clk_msec));
    clock_sec sCLK (.clk_msec(clk_msec), .clk_sec(clk_sec));
    clock_min MCLK (.clk_sec(clk_sec), .clk_min(clk_min));
    
    D_flip_flop_posedge dff_sec (.D(btn[0]), .E(clk_msec), .Q(debounced_btn[0]));
    D_flip_flop_posedge dff_min (.D(btn[1]), .E(clk_msec), .Q(debounced_btn[1]));
    
    xor (inc_sec, clk_sec, debounced_btn[0]);
    xor (inc_min, clk_min, debounced_btn[1]);
    
    
    always @(negedge inc_sec) begin
        if(sec[0] >= 9) begin
            sec[0] = 0;
            if (sec[1] >= 5)begin
                sec[1] = 0;
            end
            else sec[1] = sec[1] + 1;
        end
        else sec[0] = sec[0] + 1;
    end
    
    always @(negedge inc_min) begin
        if(min[0] >= 9) begin
            min[0] = 0;
            if (min[1] >= 5)begin
                min[1] = 0;
            end
            else min[1] = min[1] + 1;
        end
        else min[0] = min[0] + 1;
    end
    
    FND_4digit_switcher FND_4digit (
    .value_1(sec[0]),
    .value_10(sec[1]),
    .value_100(min[0]),
    .value_1000(min[1]),
    .clk(clk),
    .com_an(com_an),
    .seg(seg)
    );
    
endmodule

watch 코드

 D_flip_flop_posedge dff_sec (.D(btn[0]), .E(clk_msec), .Q(debounced_btn[0]));

부분에서 Q는 임피던스.

플립플롭은 초기화가 안돼서 0101으로 시작함.

 

`timescale 1ns / 1ps


module clock_usec(
    input clk,
    output reg clk_usec
    );
    
    reg [6:0] cnt_usec = 0;
    wire clk_100MHz;
    assign clk_100MHz = clk;
    
    always @ (negedge clk_100MHz)begin  //us
        if(cnt_usec >= 99) cnt_usec = 0;
        else cnt_usec = cnt_usec + 1;
        if(cnt_usec <= 49) clk_usec = 0;  
        else clk_usec = 1;
    end
    
 endmodule
 
 
module clock_msec(
    input clk_usec,
    output reg clk_msec
    );
    
    reg [9:0] cnt_msec = 0;
    
    always @ (negedge clk_usec)begin  //us
        if(cnt_msec >= 999) cnt_msec = 0;
        else cnt_msec = cnt_msec + 1;
        if(cnt_msec <= 499) clk_msec = 0;  
        else clk_msec = 1;
    end
    
endmodule



module clock_sec(
    input clk_msec,
    output reg clk_sec
    );
    
    reg [5:0] cnt_sec = 0;
    
    always @ (negedge clk_msec)begin  //us
        if(cnt_sec >= 999) cnt_sec = 0;
        else cnt_sec = cnt_sec + 1;
        if(cnt_sec <= 499) clk_sec = 0;  
        else clk_sec = 1;
    end
    
endmodule


module clock_min(
    input clk_sec,
    output reg clk_min
);
    
    reg [5:0] cnt_min = 0;
    
    always @(negedge clk_sec) begin
        if(cnt_min >= 59) cnt_min = 0;
        else cnt_min = cnt_min + 1;
        if (cnt_min <= 29) clk_min = 0;
        else clk_min = 1;
    end

endmodule

clock library

 

 

 

 

버튼으로 59초 넘기면 1분 오르게 하기

 

 

빨간 네모 칸을 inc_sec으로 바꿔줬다.

 

module stop_watch_top(
    input clk,
    input [2:0] btn,
    output [3:0] com_an,
    output [6:0] seg,
    output reg [3:0] led
    );
    
    wire btn_start, btn_lap, btn_clear;
    wire reset_n, start, clk_start;
    
    reg [3:0] sec [1:0];
    reg [3:0] min [1:0];
    
    
    
    not (reset_n, btn_clear); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
    
    T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
    
    and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
    
    always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤 
        if(!reset_n)begin
            sec[0] = 0;
            sec[1] = 0;
            end
            else begin
             if(sec[0] >= 9) begin
            sec[0] = 0;
            if (sec[1] >= 5)begin
                sec[1] = 0;
            end
            else sec[1] = sec[1] + 1;
        end
        else
            sec[0] = sec[0] + 1;
        end
    end

    always @(negedge clk_min or negedge reset_n) begin
        if(!reset_n) begin
            min[0] = 0;
            min[1] = 0; 
        end
        else begin
        if(min[0] >= 9) begin
            min[0] = 0;
            if (min[1] >= 5)begin
                min[1] = 0;
            end
            else min[1] = min[1] + 1;
        end  
            else min[0] = min[0] + 1;
    end
    end
    
    D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start));  //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
    D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap)); 
    D_flip_flop_posedge dff_clear (.D(btn[2]), .E(clk_msec), .Q(btn_clear)); 
    
    
     clock_usec uCLK (.clk(clk), .clk_usec(clk_usec));
    clock_msec mCLK (.clk_usec(clk_usec), .clk_msec(clk_msec));
    clock_sec sCLK (.clk_msec(clk_start), .clk_sec(clk_sec));
    clock_min MCLK (.clk_sec(clk_sec), .clk_min(clk_min));
    
    FND_4digit_switcher FND_4digit (
    .value_1(sec[0]),
    .value_10(sec[1]),
    .value_100(min[0]),
    .value_1000(min[1]),
    .clk(clk),
    .com_an(com_an),
    .seg(seg)
    );
    
    
    
    
    
    
endmodule

clear 기능을 넣어 stopwatch를 만듦.

그러나 cnt_min을 초기화 해주지 않아(다른 코드에 있음) 5초에 clear를 누르면 55초에 1분으로 변함

cnt_min을 초기화 해주는 기능 추가.

 

 

 

 

 

 

다음과 같이 reset_n을 통해 cnt_min을 0으로 초기화 해준다.

cnt_min 뿐만 아니다. 모든 시간 관련 코드를 reset_n = 0으로 만들어야 한다.

always문을 따로 만들면 안되고 같이 해줘야 한다.

 

 

 

`timescale 1ns / 1ps

module clock_usec(
    input clk,
    input reset_n,
    output reg clk_usec
    );
    
    reg [6:0] cnt_usec = 0;
    wire clk_100MHz;
    assign clk_100MHz = clk;
    
    always @ (negedge clk_100MHz or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_usec = 0;
        end
            else begin
            if(cnt_usec >= 99) cnt_usec = 0;
            else cnt_usec = cnt_usec + 1;
            if(cnt_usec <= 49) clk_usec = 0;  
            else clk_usec = 1;
        end
    end
 endmodule
 
 
module clock_msec(
    input clk_usec,
    input reset_n,
    output reg clk_msec
    );
    
    reg [9:0] cnt_msec = 0;
    
    always @ (negedge clk_usec or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_msec = 0;
        end
        else begin
            if(cnt_msec >= 999) cnt_msec = 0;
            else cnt_msec = cnt_msec + 1;
            if(cnt_msec <= 499) clk_msec = 0;  
            else clk_msec = 1;
        end
    end
endmodule



module clock_sec(
    input clk_msec,
    input reset_n,
    output reg clk_sec
    );
    
    reg [9:0] cnt_sec = 0;
    
    always @ (negedge clk_msec or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_sec = 0;
        end
        else begin
            if(cnt_sec >= 999) cnt_sec = 0;
            else cnt_sec = cnt_sec + 1;
            if(cnt_sec <= 499) clk_sec = 0;  
            else clk_sec = 1;
        end
    end
endmodule


module clock_min(
    input clk_sec,
    input reset_n,
    output reg clk_min
);
    
    reg [5:0] cnt_min = 0;
    
    always @(negedge clk_sec or negedge reset_n) begin  //always문을 따로 만들면 안됨
        if(!reset_n)begin
            cnt_min = 0;
        end
        else begin
            if(cnt_min >= 59) cnt_min = 0;
            else cnt_min = cnt_min + 1;
            if (cnt_min <= 29) clk_min = 0;
            else clk_min = 1;
        end
    end

endmodule

clock_lib를 다시 만들어줬다.

 

reset_n을 모든 cnt에 추가하였다.

reset_n을 사용해 시간들을 0으로 초기화 안해주면 0으로 초기화가 안된 상태에서 작동하기 때문에

ex)0에서 55초까지 실행하고 clear 후 5초를 실행하면 1분으로 바뀐다.

이러한 현상을 막기위해 시간 관련 코드를 전부 0으로 초기화 해줘야 막을 수 있다.

 

`timescale 1ns / 1ps

module FND_4digit_switcher( // 1ms에 1번씩 com_an 단자 바꿔줌
    input [3:0] value_1,
    input [3:0] value_10,
    input [3:0] value_100,
    input [3:0] value_1000,
    input clk,
    output reg [3:0] com_an = 4'b1110,
    output [6:0] seg
    );
    

    wire clk_usec, clk_msec;
    reg [3:0] hex_value;
    
    clock_usec UCLK (.clk(clk), .reset_n(1), .clk_usec(clk_usec));
    clock_msec MCLK (.clk_usec(clk_usec), .reset_n(1), .clk_msec(clk_msec));
    
    decoder_7_seg dec7seg(.hex_value(hex_value), .seg(seg));
    
    always @(negedge clk_msec) begin
        case (com_an)
            4'b1110: begin
                com_an = 4'b1101;
                hex_value = value_10;
            end
            4'b1101: begin
                com_an = 4'b1011;
                hex_value = value_100;
            end
            4'b1011: begin
                com_an = 4'b0111;
                hex_value = value_1000;
            end
            4'b0111: begin
                com_an = 4'b1110;
                hex_value = value_1;
            end
            default : begin
                com_an = 4'b1110;
                hex_value = value_1;
            end
        endcase
    end
    
endmodule

7세그먼트 또한 바꿔줌.

 

 

`timescale 1ns / 1ps

module stop_watch_top(
    input clk,
    input [2:0] btn,
    output [3:0] com_an,
    output [6:0] seg,
    output reg [3:0] led
    );
    
    wire btn_start, btn_lap, btn_clear;
    wire reset_n, start, clk_start;
    
    reg [3:0] sec [1:0];
    reg [3:0] min [1:0];
    
    
    
    not (reset_n, btn[2]); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
                                 
    
    T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
    
    and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
    
    always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤 
        if(!reset_n)begin
            sec[0] = 0;
            sec[1] = 0;
            end
            else begin
             if(sec[0] >= 9) begin
            sec[0] = 0;
            if (sec[1] >= 5)begin
                sec[1] = 0;
            end
            else sec[1] = sec[1] + 1;
        end
        else
            sec[0] = sec[0] + 1;
        end
    end

    always @(negedge clk_min or negedge reset_n) begin
        if(!reset_n) begin
            min[0] = 0;
            min[1] = 0; 
        end
        else begin
        if(min[0] >= 9) begin
            min[0] = 0;
            if (min[1] >= 5)begin
                min[1] = 0;
            end
            else min[1] = min[1] + 1;
        end  
            else min[0] = min[0] + 1;
    end
    end
    
    D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start));  //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
    D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap)); 
    
    
    clock_usec uCLK (.clk(clk), .reset_n(reset_n), .clk_usec(clk_usec));
    clock_msec mCLK (.clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
    clock_sec sCLK (.clk_msec(clk_start), .reset_n(reset_n), .clk_sec(clk_sec));
    clock_min MCLK (.clk_sec(clk_sec), .reset_n(reset_n), .clk_min(clk_min));
    
    FND_4digit_switcher FND_4digit (
    .value_1(sec[0]),
    .value_10(sec[1]),
    .value_100(min[0]),
    .value_1000(min[1]),
    .clk(clk),
    .com_an(com_an),
    .seg(seg)
    );
    
    
    
    
    
    
endmodule

시간 관련 함수를 0으로 초기화 해주고 진행하면 clear시 겹치지 않고 다시 시간이 start됨

 

30초가 넘으면  1분이 나오는 현상찾기

 

29초 이하일 때는 

 else begin
            if(cnt_min >= 59) cnt_min = 0;
            else cnt_min = cnt_min + 1;
            if (cnt_min <= 29) clk_min = 0;
            else clk_min = 1;
        end

위 코드 두 번째 if문에서 하강에지가 발생하기 때문에

 else min[0] = min[0] + 1;

stop_watch 코드에서 위와 같은 코드에 의해 1이 출력됨.

 

 

`timescale 1ns / 1ps

module clock_usec(
    input clk,
    input reset_n,
    output reg clk_usec
    );
    
    reg [6:0] cnt_usec = 0;
    wire clk_100MHz;
    assign clk_100MHz = clk;
    
    always @ (negedge clk_100MHz or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_usec = 0;
        end
            else begin
            if(cnt_usec >= 99) cnt_usec = 0;
            else cnt_usec = cnt_usec + 1;
            if(cnt_usec < 99) clk_usec = 0;  
            else clk_usec = 1;
        end
    end
 endmodule
 
 
module clock_msec(
    input clk_usec,
    input reset_n,
    output reg clk_msec
    );
    
    reg [9:0] cnt_msec = 0;
    
    always @ (negedge clk_usec or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_msec = 0;
        end
        else begin
            if(cnt_msec >= 999) cnt_msec = 0;
            else cnt_msec = cnt_msec + 1;
            if(cnt_msec < 999) clk_msec = 0;  
            else clk_msec = 1;
        end
    end
endmodule



module clock_sec(
    input clk_msec,
    input reset_n,
    output reg clk_sec
    );
    
    reg [9:0] cnt_sec = 0;
    
    always @ (negedge clk_msec or negedge reset_n)begin  //us
        if(!reset_n)begin
            cnt_sec = 0;
        end
        else begin
            if(cnt_sec >= 999) cnt_sec = 0;
            else cnt_sec = cnt_sec + 1;
            if(cnt_sec < 999) clk_sec = 0;  
            else clk_sec = 1;
        end
    end
endmodule


module clock_min(
    input clk_sec,
    input reset_n,
    output reg clk_min
);
    
    reg [5:0] cnt_min = 0;
    
    always @(negedge clk_sec or negedge reset_n) begin  //always문을 따로 만들면 안됨
        if(!reset_n)begin
            cnt_min = 0;
        end
        else begin
            if(cnt_min >= 59) cnt_min = 0;
            else cnt_min = cnt_min + 1;
            if (cnt_min < 59) clk_min = 0;
            else clk_min = 1;
        end
    end

endmodule

오류를 막기위해 시간 관련 코드를 49 => 99, 499 => 999로 바꿔준다.

물론 완전히 오류가 잡히는 것은 아닌데 확률이 1/1000으로 줄어든다. (임시방편)

1ms는 미미해서 눈으로 보기 힘들다.

 

손으로 누르는 것이기 때문에 시간이 길어서 가능한 것.

만약 clk을 줘서 실행하는거면 다른 기능을 써야하는데 아직 못배움. 

 

clock_lib에서 각 시간마다 clk_시간 쪽을 초기화 해줘도 된다.

어디까지나 임시방편일뿐, 배우지 못한 진도가 있어 자세하게 오류를 잡긴 못한다.

 

 always @(negedge clk_sec or negedge reset_n) begin  //always문을 따로 만들면 안됨
        if(!reset_n)begin
            cnt_min = 0;
           clk_min = 0;
        end
        else begin
            if(cnt_min >= 59) cnt_min = 0;
            else cnt_min = cnt_min + 1;
            if (cnt_min < 59) clk_min = 0;
            else clk_min = 1;
        end
    end

 

 

코드 수정 후 30초가 지난 후에 clear 하고 start를 눌러도 잘 작동하는 모습

 

 

lap기능 추가하기

 

`timescale 1ns / 1ps

module stop_watch_top(
    input clk,
    input [2:0] btn,
    output [3:0] com_an,
    output [6:0] seg,
    output reg [3:0] led
    );
    
    wire btn_start, btn_lap, btn_clear;
    wire reset_n, start, clk_start;
    
    reg [3:0] sec [1:0];
    reg [3:0] min [1:0];
    reg [3:0] lap [3:0]; //lap btn을 누르면 멈춘 시간을 보여주지만 시간은 가는 기능
    reg [3:0] out_value [3:0];
    
    always @ (lap_e)begin
        if(lap_e)begin             //mux : 조합회로
            out_value[0] = lap[0];
            out_value[1] = lap[1];
            out_value[2] = lap[2];
            out_value[3] = lap[3];
        end
        else begin
            out_value[0] = sec[0];
            out_value[1] = sec[1];
            out_value[2] = min[2];
            out_value[3] = min[3];
        end
    end
    always @ (posedge btn_lap or negedge reset_n)begin  //손으로 눌렀다 뗐을 때 현재의 값으로 바뀌니 posedge
        if(!reset_n)begin
            lap[0] = 0;
            lap[1] = 0;
            lap[2] = 0;
            lap[3] = 0;
        end
        else begin
            lap[0] = sec[0];
            lap[1] = sec[1];
            lap[2] = min[0];
            lap[3] = min[1];
        end
    end
    not (reset_n, btn[2]); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
                                 
    
    T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
    
    T_flip_flop_posedge tff_lap (.T(1), .clk(btn_lap), .reset_n(reset_n), .preset_n(1), .Q(lap_e)); //preset은 0에서 작동하니 1을 준다.
                                                                                                                  //lap_enable
    and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
    
    always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤 
        if(!reset_n)begin
            sec[0] = 0;
            sec[1] = 0;
            end
            else begin
             if(sec[0] >= 9) begin
            sec[0] = 0;
            if (sec[1] >= 5)begin
                sec[1] = 0;
            end
            else sec[1] = sec[1] + 1;
        end
        else
            sec[0] = sec[0] + 1;
        end
    end

    always @(negedge clk_min or negedge reset_n) begin
        if(!reset_n) begin
            min[0] = 0;
            min[1] = 0; 
        end
        else begin
        if(min[0] >= 9) begin
            min[0] = 0;
            if (min[1] >= 5)begin
                min[1] = 0;
            end
            else min[1] = min[1] + 1;
        end  
            else min[0] = min[0] + 1;
    end
    end
    
    D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start));  //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
    D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap)); 
    
    
    clock_usec uCLK (.clk(clk), .reset_n(reset_n), .clk_usec(clk_usec));
    clock_msec mCLK (.clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
    clock_sec sCLK (.clk_msec(clk_start), .reset_n(reset_n), .clk_sec(clk_sec));
    clock_min MCLK (.clk_sec(clk_sec), .reset_n(reset_n), .clk_min(clk_min));
    
    FND_4digit_switcher FND_4digit (
    .value_1(out_value[0]),
    .value_10(out_value[1]),
    .value_100(out_value[2]),
    .value_1000(out_value[3]),
    .clk(clk),
    .com_an(com_an),
    .seg(seg)
    );
    
    
    
    
    
    
endmodule

 

lap기능 추가 코드