Language/Verilog

Vivado : Basys3 Stop watch

짱도르딘 2024. 7. 18. 19:38
728x90

Stop Watch

이번에는 여러 가지 기능을 가진 스톱워치를 구현해 볼 예정이다.


아래 구현할 스톱워치는 리셋을 누르면 0으로 초기화됨과 동시에 시간이 멈추며, 스위치(버튼)를 누를 때 시간이 멈추고, 다시 누르면 시간이 흐르는 동작을 수행한다.

 

스톱워치의 코드는 아래와 같다.

 

1 sec이 흐를 수 있도록 이전글에서 구현한 clock 코드를 가져와 사용하였고, 0번 버튼을 스위치로 사용하기 위해 "btn [0]"을 선언하였다.

 

이전글을 살펴보면 clock이 어떻게 동작하는지 설명되어 있다.

 

 

Vivado : Basys3 FND / 디지털 시계, 분주

□ 본 게시글은 수업 내용을 바탕으로 주관적으로 작성한 것이기 때문에, 틀리거나 오류가 있을 수 있습니다!!!  FND Control FND 출력을 링카운터를 활용해 시프트 동작을 시켜 1110 -> 1101 -> 1011 -> 0

jangdong.tistory.com

 

T F/F을 사용하여 시계의 멈춤과 동작을 구현하였고, LED가 ON일시에는 시계가 동작, LED가 OFF일시에는 시계가 멈추는 것을 표현하였다.

 

module stop_watch_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output led_start);
    
    wire start_stop;
    wire clk_start;
    wire btn_start;
    wire [3:0] min10, min1, sec10, sec1;
    wire clk_usec, clk_msec, clk_sec, clk_min;
    
    assign clk_start = start_stop ? clk : 0;
    
    // clock
    clock_div_100 usec_clk(.clk(clk_start), .reset_p(reset_p), .clk_div_100(clk_usec));
    clock_div_1000 msec_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_msec), .clk_div_1000_nedge(clk_sec));
    clock_div_60 min_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_sec), .clk_div_60_nedge(clk_min));
    
    // button Mode    
    button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_start));
//    button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_sec));
//    button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_min));
    
    // button control
    T_flip_flop_p t_start(.clk(clk), .reset_p(reset_p), .t(btn_start), .q(start_stop));
    assign led_start = start_stop; // LED ON = START, LED OFF = STOP
    
    counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .bcd1(sec1), .bcd10(sec10));
    counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .bcd1(min1), .bcd10(min10));
    
    // Fnd controler
    wire [15:0] value;
    assign value = {min10, min1, sec10, sec1};
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
    
endmodule

 

스톱워치의 스위치를 누를 때마다 LED를 작동시키기 위해서 Basys3 xdc에 들어가 아래와 같이 LED 0번을 선언하였다.

Basys3 xdc

 

위 코드를 실행시키면 아래와 같은 동작을 수행한다.

 

 

 


 

Stop watch( + lap 기능)

이번에는 lap 기능이 추가된 스톱워치를 구현하고자 한다.

lap 기록 버튼을 눌렀을 때, 누른 시점의 시간을 저장하며 멈춘다. 그러고 다시 lap 버튼을 누르면 lap버튼을 처음 눌렀던 시점부터 흘렀던 시간만큼 다시 시간이 추가되어 스톱워치의 기능을 수행한다.

 

코드는 아래와 같다.

 

위의 스톱워치 코드에서 하나의 T F/F이 추가된 것을 확인할 수 있다. Toggle이 될 때마다 lap 기능을 수행하며, 다시 Toggle 되었을 때에는 lap 기능을 멈추는 동작을 수행한다.

 

lap 기능이 실행될 때에는 2번째 LED에 점등이 되며, lap 기능이 멈추었을 때에는 LED는 꺼지는 동작을 수행한다.

 

module stop_watch_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output led_start, led_lap);
    
    wire start_stop, lap;
    wire clk_start;
    wire btn_start, btn_lap;
    wire clk_usec, clk_msec, clk_sec, clk_min;
    
    assign clk_start = start_stop ? clk : 0;
    
    // clock
    clock_div_100 usec_clk(.clk(clk_start), .reset_p(reset_p), .clk_div_100(clk_usec));
    clock_div_1000 msec_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_msec), .clk_div_1000_nedge(clk_sec));
    clock_div_60 min_clk(.clk(clk_start), .reset_p(reset_p), .clk_source(clk_sec), .clk_div_60_nedge(clk_min));
    
    // button Mode    
    button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_start));
    button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_lap));
//    button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_min));
    
    // button control
    T_flip_flop_p t_start(.clk(clk), .reset_p(reset_p), .t(btn_start), .q(start_stop));
    assign led_start = start_stop; // LED ON = START, LED OFF = STOP
    
    // Lap control
    T_flip_flop_p t_lap(.clk(clk), .reset_p(reset_p), .t(btn_lap), .q(lap));
    assign led_lap = lap;
            
    wire [3:0] min10, min1, sec10, sec1;
    counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .bcd1(sec1), .bcd10(sec10));
    counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .bcd1(min1), .bcd10(min10));
    
    // Time Save Register
    reg [15:0] lap_time;
    wire [15:0] cur_time;
    assign cur_time = {min10, min1, sec10, sec1};
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)lap_time = 0;
        else if(btn_lap)lap_time = {min10, min1, sec10, sec1};
    end
        
    // Fnd controler
    wire [15:0] value;
    assign value = lap ? lap_time : cur_time;
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
    
endmodule

 

 

lap이 실행되었을 때 LED 점등을 위해서 아래와 같이 xdc에 led_lap을 선언하였다.

 

다음은 코드를 실행시켰을 때에 동작을 나타낸다.


Stop watch( + clear btn)

 

이번에는 lab 기능이 추가된 스톱워치에서 lab 기록을 clear 할 수 있는 clear 스톱워치를 구현하였다.

clear를 초기화 하기 위해서 60진 카운터에 클리어를 추가한 후, 클리어에 버튼 클리어를 인가해주었다.

 

lap 기능을 구현하기 위하여 "reg lap"을 선언하였고, 코드 내 두 개의 T F/F을 합쳐 하나의 플립플롭으로 구성하였다.

 

"reset_start"에는 OR 연산자로 reset_p와 btn_clear를 묶어 clock을 구성하는 모듈에 하나씩 인가하였다.

 

clear버튼을 누르면 나타나는 시간과 lap이 clear되는 것을 확인할 수 있다.

 

clear과 reset의 기능이 동일한 것 같지만, reset은 모듈 전체를 reset 하는 반면, clear는 clear을 하고 싶은 동작을 선택할 수 있다는 차이점이 있다.

module stop_watch_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output led_start, led_lap);
    
    reg lap;
    wire start_stop;
    wire clk_start;
    wire btn_start, btn_lap, btn_clear;
    wire clk_usec, clk_msec, clk_sec, clk_min;
    wire reset_start;
    
    assign clk_start = start_stop ? clk : 0;
    
    // clock
    clock_div_100 usec_clk(.clk(clk_start), .reset_p(reset_start), .clk_div_100(clk_usec));
    clock_div_1000 msec_clk(.clk(clk_start), .reset_p(reset_start), .clk_source(clk_usec), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk_start), .reset_p(reset_start), .clk_source(clk_msec), .clk_div_1000_nedge(clk_sec));
    clock_div_60 min_clk(.clk(clk_start), .reset_p(reset_start), .clk_source(clk_sec), .clk_div_60_nedge(clk_min));
    
    // button Mode    
    button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_start));
    button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_lap));
    button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_clear));
    
    assign reset_start = reset_p | btn_clear;
    
    // button/lap control
    T_flip_flop_p t_start(.clk(clk), .reset_p(reset_start), .t(btn_start), .q(start_stop));
    assign led_start = start_stop; // LED ON = START, LED OFF = STOP
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)lap = 0;
        else begin
            if(btn_lap) lap = ~lap;
            else if(btn_clear) lap = 0;
        end
    end    
    
    // Lap control
    assign led_lap = lap;
            
    wire [3:0] min10, min1, sec10, sec1;
    counter_bcd_60_clear counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .clear(btn_clear), .bcd1(sec1), .bcd10(sec10));
    counter_bcd_60_clear counter_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .clear(btn_clear) , .bcd1(min1), .bcd10(min10));
    
    // Time Save Register
    reg [15:0] lap_time;
    wire [15:0] cur_time;
    assign cur_time = {min10, min1, sec10, sec1};
    always @(posedge clk or posedge reset_p)begin
        if(reset_p) lap_time = 0;
        else if(btn_lap) lap_time = cur_time;
        else if(btn_clear) lap_time = 0;
    end
        
    // Fnd controler
    wire [15:0] value;
    assign value = lap ? lap_time : cur_time;
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
    
endmodule

 

위 코드를 실행시키면 아래와 같은 동작을 수행하는 것을 확인할 수 있다.

 

 


이번에는 ms까지 표현할 수 있는 시계를 구현해보려 한다.

 

4개의 FND는 각각 10초단위, 1초 단위, 10ms단위, 1ms단위를 나타낸다. 따라서 10ms를 표현하기 위해 10진 카운터를 설정하였다. 10진 카운터는 아래의 코드로 구현하였다.

 

clock이 9 이상일 시 0으로 초기화 됨과 동시에 clock source에 1을 추가하는 동작을 수행한다. 

module clock_div_10(
    input clk, reset_p,
    input clk_source,
    output clk_div_10,
    output clk_div_10_nedge);
    
    reg [3:0] cnt_clksource;
    wire clk_source_nedge;
    
    edge_detector_n ed_source(
    .clk(clk), .reset_p(reset_p), .cp(clk_source),
    .n_edge(clk_source_nedge));    
    
    always @(negedge clk or posedge reset_p)begin
        if(reset_p)cnt_clksource = 0;
        else if(clk_source_nedge)begin
            if(cnt_clksource >= 9) cnt_clksource = 0;
            else cnt_clksource = cnt_clksource + 1;
        end    
    end
    
    assign clk_div_10 = (cnt_clksource < 5) ? 0 : 1;
    
    edge_detector_n ed(
    .clk(clk), .reset_p(reset_p), .cp(clk_div_10),
    .n_edge(clk_div_10_nedge));
    
endmodule

 

아래는 10진 카운터를 포함한 시계를 동작시킬 코드이다.

 

Basys3는 10ns를 기본적으로 지니고 있기 때문에 1 sec을 만들기 위해서 "clock_div" 부분과 같이 분주비를 설정하였다.

분주는 sec까지만을 표현하며, 분주비에 대한 코드는 이전글을 참고하면 된다.

 

버튼의 모드는 총 3가지로, stop/start, lap 그리고 clear동작을 수행한다. 

 

"assign cur_time" 부분이 FND에 숫자를 표시할 순서이며 차례대로 10 sec, 1 sec, 10 msec 그리고 1 msec로 표현하였다.

 

module stop_watch_ms_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output led_start, led_lap);
    
    reg lap;
    wire start_stop;
    wire clk_start;
    wire btn_start, btn_lap, btn_clear;
    wire clk_usec, clk_msec, clk_10msec, clk_sec;
    wire reset_start;
    
    assign clk_start = start_stop ? clk : 0;
    
    // clock
    clock_div_100 usec_clk(.clk(clk_start), .reset_p(reset_start), .clk_div_100(clk_usec));
    clock_div_1000 msec_clk(.clk(clk_start), .reset_p(reset_start), .clk_source(clk_usec), .clk_div_1000(clk_msec));
    clock_div_10 cm_clk(.clk(clk_start), .reset_p(reset_start),.clk_source(clk_msec), .clk_div_10(clk_10msec));
    clock_div_1000 sec_clk(.clk(clk_start), .reset_p(reset_start), .clk_source(clk_msec), .clk_div_1000_nedge(clk_sec));
    
    // button Mode    
    button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_start));
    button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_lap));
    button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_clear));
    
    assign reset_start = reset_p | btn_clear;
    
    // button/lap control
    T_flip_flop_p t_start(.clk(clk), .reset_p(reset_start), .t(btn_start), .q(start_stop));
    assign led_start = start_stop; // LED ON = START, LED OFF = STOP
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)lap = 0;
        else begin
            if(btn_lap) lap = ~lap;
            else if(btn_clear) lap = 0;
        end
    end    
    
    // Lap control
    assign led_lap = lap;
            
    wire [3:0] sec10, sec1, mil10, mil1;
    counter_bcd_100_clear counter_mil(.clk(clk), .reset_p(reset_p), .clk_time(clk_10msec), .clear(btn_clear), .bcd1(mil1), .bcd10(mil10));
    counter_bcd_60_clear counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .clear(btn_clear) , .bcd1(sec1), .bcd10(sec10));
    
    // Time Save Register
    reg [15:0] lap_time;
    wire [15:0] cur_time;
    assign cur_time = {sec10, sec1, mil10, mil1};
    always @(posedge clk or posedge reset_p)begin
        if(reset_p) lap_time = 0;
        else if(btn_lap) lap_time = cur_time;
        else if(btn_clear) lap_time = 0;
    end
        
    // Fnd controler
    wire [15:0] value;
    assign value = lap ? lap_time : cur_time;
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
    
endmodule

 

위 코드를 동작시키면 아래와 같은 결과를 출력하는 것을 확인할 수 있다.

 

 

728x90

'Language > Verilog' 카테고리의 다른 글

Vivado : 키패드  (0) 2024.07.25
Vivado : 초음파 센서  (0) 2024.07.25
Vivado : Basys3 FND / 디지털 시계, 분주  (0) 2024.07.17
Vivado : Basys3 7segment  (0) 2024.07.16
Vivado : Edge Detector, Shift Register(SISO, SIPO, PISO, PIPO)  (2) 2024.07.16