본문 바로가기

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 18일 - 3, Stop Watch - 2)

1. Lap 기능 추가하기

 

Verilog RTL 설계(7월 18일 - 2, Stop Watch - 1)

1. Stop Watch 이전 게시글에서 다루었던 Clock 지식을 기반으로 Stop Watch을 구현해보도록 하겠다.단계별로 기능을 하나씩 추가해 나아가도록 하겠다.   2. Basic Stop Watch기본적인 Stop Watch는 다음과

jbhdeve.tistory.com

  • 이번 구현은 다음과 같이 동작한다.
    - 랩 버튼을 누르면 현재의 시간에서 멈춰 현재의 시간을 기록하여 디스플레이에 표시한다.
    - 주의 할 점은 비록 디스플레이 상에서는 시간은 멈춰 있지만, Stop Watch 모드에서는 시간은 계속 지나고 있다.는 점이다.
    - 다시 랩 버튼을 누르면 기록했던 시간은 없어지고, Stop Watch 모드에서 지나갔던 시간이 이어서 디스플레이에 표시하게 된다.
    - 간단히 이해하기 쉽게 휴대폰 및 컴퓨터에 내장된 스톱 워치 기능을 떠올리면 된다.

 

 

 

2. Lap 기능을 추가하기 위해서는 어떻게 코드를 수정해야 하는가?

  • Lap 기능을 위한 코드 수정은 코드와 함께 설명하도록 하겠다.

 

< Source , Add Lap button >

// Control Stop Watch
wire btn_start, btn_lap;
button_cntr control_btn_set (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_nedge(btn_start));
button_cntr control_btn_lap (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_nedge(btn_lap));
  • lap 모드를 컨트롤 하기 위해 btn[1]을 추가한다.
    - btn[1]는 button_cntr 모듈을 이용하여 btn_lap 변수를 얻는다.
    - btn_lap 변수는 lap 버튼이 눌러 졌을 때, 이를 한 싸이클 동안 활성화시켜 알려주는 One Cycle Pulse이다.
  • button_cntr 모듈의 역활과 모듈 구조에 대해서 궁금하다면 아래 게시글을 참고하기 바란다.
    https://jbhdeve.tistory.com/267
 

Verilog RTL 설계(7월 17일 - 6, Advanced Clock Mode - 3)

1.  Chattering 문제 발생  1.1. 문제의 원인button을 눌러 초나 분 값을 증가시키고자 하는데, Chattering으로 인해 한 번의 버튼 누름으로 값이 2 이상 씩 증가한다.chattering 문제는 다양한 원인을 갖고

jbhdeve.tistory.com

 

 

 

 

< Source , Control Lap mode>

// Control Lap mode
wire lap;
t_flip_flop t_flip_flop1 (.clk(clk), .reset_p(reset_p), .t(btn_lap), .q(lap));
  • lap 버튼이 누르게 된다면 lap 버튼의 One Cycle Pulse인 btn_lap을 통해 신호를 주게 된다.
  • 따라서 btn_lap 값을 T-Flip Flop의 입력값으로 주게 된다면 lap 버튼이 눌러질 때마다  T Flip-Flop에 의해 lap 모드가 변화하게 된다.

 

 

< Source, Choose hex value >

// Combine data.
wire [15:0] hex_value, cur_time;
reg [15:0] lap_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;
end
    
assign hex_value = (lap)? lap_time : cur_time;
  • always 문내에 btn_lap이 활성화 되었을 때, lap 버튼이 눌렀음을 인식하고, lap_time 변수에 cur_time 값을 대입하게 된다,
  • MUX을 이용하여 lap 값에 따라 최종적으로 디스플레이 출력되는 값을 선택한 뒤, hex_value에 대입하게 된다.
  • Q) 왜 btn_lap 값에 따라 lap_time에 대입하는 여부가 결정하는가? lap 값에 따라 결정하면 안되는가?
    A) Lap 모드에 들어오게 되면 Lap 버튼을 눌렀던 순간의 시간이 디스플레이에 출력되며, 이는 Lap 버튼을 누르기 전까지 변화하지 않는다.

    만약 Lap 값에 따라 결정된다면 Lap 버튼 누르는 순간 Lap 값은 1을 갖게 되고, Lap 모드로 들어가게 되면 시간은 정지되어야 하지만, 계속해서 cur_time 변수로부터 값을 받기 때문에 시간이 정지하지 않고, 계속 해서 증가하는 모습을 관찰할 수 있다. 

    Lap 모드로 들어갔을 때, 시간을 정지시키기 위해서는 Lap 버튼을 누르는 순간에만 cur_time 변수로 부터 값을 받아야 하며, 이 후에는 값을 받지 않고, 일정하게 유지시켜 줘야 한다. 따라서 이를 위해서는 lap 값 대신 btn_lap 변수를 이용하는 것이 더 적합하다고 볼 수 있다.

 

 

 

3. 구현 영상 

 

 

 

 

 

4. 현재까지의 전체 소스 코드

module Stop_Watch(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7 );
    
    // Control Stop Watch
    wire btn_start, btn_lap;
    button_cntr control_btn_set (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_nedge(btn_start));
    button_cntr control_btn_lap (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_nedge(btn_lap));
    
    // Control Start / Stop mode.
    wire start_stop;
    t_flip_flop t_flip_flop_0 (.clk(clk), .reset_p(reset_p), .t(btn_start), .q(start_stop));
    
    // Control Lap mode
    wire lap;
    t_flip_flop t_flip_flop1 (.clk(clk), .reset_p(reset_p), .t(btn_lap), .q(lap));
    
    // Control clk.
    wire clk_start;
    assign clk_start = (start_stop)? clk : 0;
    
    // Get One Cycle Pulse of Second    , Minute
    wire clk_usec, clk_sec, clk_min;
    clk_div_100 clk_div_usec (.clk(clk_start), .reset_p(reset_p), .clk_div_100(clk_usec));
    clk_div_n clk_div_sec(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .prescale(1000000), .clk_div_n_nedge(clk_sec));
    clk_div_n clk_div_min(.clk(clk), .reset_p(reset_p), .clk_source(clk_sec), .prescale(60), .clk_div_n_nedge(clk_min));
    
    // BCD 60 Counter
    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));
    
    // Combine data.
    wire [15:0] hex_value, cur_time;
    reg [15:0] lap_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;
    end
    
    assign hex_value = (lap)? lap_time : cur_time;
      
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(hex_value), .com(com), .seg_7(seg_7));
    
endmodule

// T Flip Flop
module t_flip_flop (
    input clk, reset_p,
    input t, 
    output reg q );
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) q = 0;
        else q = (t)? ~q : q;
    end
    
endmodule

// Control Button.
module button_cntr (
    input clk, reset_p,
    input btn,
    output btn_pedge, btn_nedge);
  
  // Get 1ms One Cycle Pulse
   reg [16:0] counter;
   always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else begin
            if(counter >= 99999) counter = 0;
            else counter = counter + 1;
        end
   end

   wire clk_1ms, clk_1ms_nedge;
   assign clk_1ms = (counter < 50000) ? 0 : 1;
   edge_detector_n edge_dector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_1ms), .n_edge(clk_1ms_nedge));
   
   //  D Flip-Flop을 이용한 button 값 읽기 
   reg debounced_btn;
   
   always @(posedge clk or posedge reset_p) begin
        if(reset_p) debounced_btn = 0;
        else if(clk_1ms_nedge) debounced_btn = btn;
   end
   
   edge_detector_n edge_dector_1 (.clk(clk), .reset_p(reset_p), .cp(debounced_btn), .n_edge(btn_nedge), .p_edge(btn_pedge));
  
endmodule

// BCD 60 Counter.
module counter_bcd_60(
    input clk, reset_p,
    input clk_time,
    output reg [3:0] bcd1, bcd10 );
    
    // Get One Cycle of clk_time
    wire clk_time_nedge;
    edge_detector_n edge_dector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_time), .n_edge(clk_time_nedge));
    
    // Counter
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            bcd1 = 0;
            bcd10 = 0;
        end
        else if(clk_time_nedge) begin
               if(bcd1 >= 9) begin
                    bcd1 = 0;
                    if(bcd10 >= 5) bcd10 = 0;
                    else bcd10 = bcd10 +1;
               end else bcd1 = bcd1 + 1;
        end
    end
    
endmodule

// Prescaler n
module clk_div_n (
    input clk, reset_p,
    input clk_source,
    input [31:0] prescale,
    output clk_div_n, clk_div_n_nedge );
    
    // Get one cycle pulse of clk_source
    wire clk_source_nedge;
    edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_source_nedge));
    
    // Counting
    integer counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else if(clk_source_nedge) begin
            if(counter >= prescale-1) counter = 0;
            else counter = counter + 1;
        end 
    end
    
    // Get clk_div_n wave, One cycle pulse of clk_div_n.
    assign clk_div_n = (counter < prescale / 2)? 0 : 1;
    edge_detector_n edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_n), .n_edge(clk_div_n_nedge));
    
endmodule

// Prescaler 100.
module clk_div_100 (
    input clk, reset_p,
    input clk_div_100,
    input clk_div_100_nedge );
    
    reg [6:0] counter;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else begin
            if(counter >= 99) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_100 = (counter < 50)? 0 : 1;
    edge_detector_n edge_dector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_div_100), .n_edge(clk_div_100_nedge));
    
endmodule

// Control FND
module fnd_cntr (
    input clk, reset_p,
    input [15:0] value,
    output reg [3:0] com,
    output [7:0] seg_7 );
    
    // prescaler 100000
    reg[16:0] counter;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else begin
            if(counter >= 99999) counter = 0;
            else counter = counter + 1;
        end
    end
    
    wire clk_1ms, clk_1ms_nedge;
    assign clk_1ms = (counter < 50000)? 0 : 1;
    edge_detector_n edge_dector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_1ms), .n_edge(clk_1ms_nedge));
    
    // Shifting com.
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) com = 4'b1110;
        else if(clk_1ms_nedge) begin
            if(com == 4'b0111) com = 4'b1110;
            else com = {com[2:0], 1'b1};
        end
    end
    
    // Get FND data.
    reg [3:0] hex_value;
    
    always @(com) begin
        case(com)
            4'b1110 : hex_value = value[3:0];
            4'b1101 : hex_value = value[7:4];
            4'b1011 : hex_value = value[11:8];
            4'b0111 : hex_value = value[15:12];
            default : hex_value = hex_value;
        endcase
    end
    
    decoder_7seg fnd(.hex_value(hex_value), .seg_7(seg_7));
endmodule

// Decoder of 7-Segment.
module decoder_7seg (
    input [3:0] hex_value,
    output reg [7:0] seg_7 );
    
    always @(hex_value) begin
        case(hex_value)
            0  : seg_7 = 8'b0000_0011; //common anode, 0이 켜지는거임 
            1  : seg_7 = 8'b1001_1111;  // 1
            2  : seg_7 = 8'b0010_0101;  // 2
            3  : seg_7 = 8'b0000_1101;  // 3
            4  : seg_7 = 8'b1001_1001;  // 4
            5  : seg_7 = 8'b0100_1001;  // 5
            6  : seg_7 = 8'b0100_0001;  // 6
            7  : seg_7 = 8'b0001_1111;  // 7
            8  : seg_7 = 8'b0000_0001;  // 8
            9  : seg_7 = 8'b0000_1001;  // 9
            10 : seg_7 = 8'b0001_0001;  // A
            11 : seg_7 = 8'b1100_0001;  // b
            12 : seg_7 = 8'b0110_0011;  // C
            13 : seg_7 = 8'b1000_0101;  // d
            14 : seg_7 = 8'b0110_0001;  // E
            15 : seg_7 = 8'b0111_0001;  // F
        endcase
    end
    
endmodule

// Edge detector 
module edge_detector_n (
    input clk, reset_p,
    input cp,
    output n_edge, p_edge);
    
    reg flip_flop_current, flip_flop_old;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            flip_flop_current <= 0;
            flip_flop_old <= 0; 
        end begin
            flip_flop_current <= cp;
            flip_flop_old <= flip_flop_current;
        end
    end
    
    assign p_edge = ({flip_flop_current, flip_flop_old} == 2'b10) ? 1 : 0;
    assign n_edge = ({flip_flop_current, flip_flop_old} == 2'b01) ? 1 : 0;
    
endmodule