거북이처럼 천천히

Verilog RTL 설계(7월 19일 - 2, Cooking Timer - 2) 본문

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 19일 - 2, Cooking Timer - 2)

유로 청년 2024. 8. 8. 22:49

1. Cooking Timer에 Buzz를 추가하자.

  • 시중에 팔고 있는 Cooking Timer는 0분 0초가 되면 알람을 울려 사용자에게 0분 0초가 되었음을 알린다.
  • 이전에 구현한 Cooking Timer에서 추가적으로 해당 기능을 추가해보도록 하겠다.

 

 

2. 수정된 소스 코드

  • 이전 게시글에서 구현한 소스 코드를 바탕으로 구현하겠으며, 수정된 코드를 중점으로 설명하도록 하겠다.
    https://jbhdeve.tistory.com/290
 

Verilog RTL 설계(7월 19일 - 1, Cooking Timer - 1)

1. Cooking Timer 꽤 늦었지만, 휴일을 통해 Cooking Timer을 구현해보도록 하겠다.Cooking Timer는 주방에서 사용하는 Timer라고 보면 이해하기 쉬울 수 있다.시간을 설정한 뒤, 1초씩 Down Counting하다가 0분 0

jbhdeve.tistory.com

 

 

 

< 알람이 울리고 있는지 여부를 LED로 표현한다. >

// Mode led
assign mode_led [1:0] = (start_set)? 2'b10 : 2'b01;
assign mode_led [2] = buzz;

 

 

 

< 알람이 울리는 조건과 알람을 꺼지는 조건을 다음과 같이 정의하였다. >

 // Start_set mode 선언 및 0분 0초가 되면 Reset하기.
reg start_set;
always @(negedge clk or posedge reset_p) begin
    if(reset_p) begin start_set = 0; buzz = 0; end
    else if(hex_value_cur == 16'b0 && start_set) begin
         start_set = 0;
         buzz = 1;
    end
    else if(btn_start_set) start_set = ~ start_set;
    else if(btn_alarm_off) buzz = 0;
end
  • start_set 변수 값이 1인 (= Start mode) 인 동시에 현재의 시간 (= hex_value_cur)이  0일 경우, start_set 값을 초기화하는 동시에 buzz = 1을 대입하여  알람과 연결된 포트에 신호를 줌으로서 알람을 울리게 만들었다.
  • btn[3], btn_alarm_off 버튼이 눌리면 buzz = 0을 대입하여 알람을 끄도록 만들었다.

 

 

 

3. 구현 영상

 

 

 

 

4. 전체 소스 코드

// Top Module of Cooking Timer
module Cooking_Timer_0(
    input clk, reset_p,
    input [3:0] btn,
    output reg buzz,
    output [3:0] com,
    output [7:0] seg_7,
    output [2:0] mode_led );
    
    // Mode led
    assign mode_led [1:0] = (start_set)? 2'b10 : 2'b01;
    assign mode_led [2] = buzz;
    
    // Get One Cycle Puls of 1sec, 1min
    wire clk_1usec, clk_1msec, clk_1sec;
    clk_div_100 clk_div_1usec (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    clk_div_1000 clk_div_1sec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1msec), .clk_div_1000_nedge(clk_1sec));

    
    // Get One Cycle Pulse of Button.
    wire btn_start_set, btn_min, btn_sec, btn_alarm_off;
    button_cntr control_btn_start(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_start_set));
    button_cntr control_btn_min(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_min));
    button_cntr control_btn_second(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_sec));
    button_cntr control_btn_alarm_off(.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_alarm_off));
    
    // Set minute, second on set mode.
    wire [3:0] bcd10_min_set, bcd1_min_set, bcd10_sec_set, bcd1_sec_set;
    bcd_60_up_counter bcd_60_up_counter_min (.clk(clk), .reset_p(reset_p), .btn(btn_min), .bcd10(bcd10_min_set), .bcd1(bcd1_min_set), .start_set(start_set));
    bcd_60_up_counter bcd_60_up_counter_sec (.clk(clk), .reset_p(reset_p), .btn(btn_sec), .bcd10(bcd10_sec_set), .bcd1(bcd1_sec_set), .start_set(start_set));
    
    
    
    // BCD 60진 Down Counter
    wire second_zero_signal;
    wire [3:0] bcd10_min_stop, bcd1_min_stop, bcd10_sec_stop, bcd1_sec_stop;
    bcd_60_down_counter bcd_60_down_counter_sec (.clk(clk), .reset_p(reset_p), .start_set(start_set), .loadable_bcd10(bcd10_sec_set), .loadable_bcd1(bcd1_sec_set),
                                                 .clk_source(clk_1sec), .sec_zero_signal(second_zero_signal), .bcd10(bcd10_sec_stop), .bcd1(bcd1_sec_stop));
    bcd_60_down_counter bcd_60_down_counter_min (.clk(clk), .reset_p(reset_p), .start_set(start_set), .loadable_bcd10(bcd10_min_set), .loadable_bcd1(bcd1_min_set),
                                                 .clk_source(second_zero_signal), .bcd10(bcd10_min_stop), .bcd1(bcd1_min_stop));
    
    // Start_set mode 선언 및 0분 0초가 되면 Reset하기.
    reg start_set;
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin start_set = 0; buzz = 0; end
        else if(hex_value_cur == 16'b0 && start_set) begin
            start_set = 0;
            buzz = 1;
        end
        else if(btn_start_set) start_set = ~ start_set;
        else if(btn_alarm_off) buzz = 0;
    end
    
    // Combine data of time.
    wire [15:0] hex_value_set, hex_value_cur;
    assign hex_value_set = {bcd10_min_set, bcd1_min_set, bcd10_sec_set, bcd1_sec_set};
    assign hex_value_cur = {bcd10_min_stop, bcd1_min_stop, bcd10_sec_stop, bcd1_sec_stop};
    
    wire [15:0] hex_value;
    assign hex_value = (start_set)? hex_value_cur : hex_value_set;
    
   // Print Data of Time to FND
    fnd_cntr control_fnd(.clk(clk), .reset_p(reset_p), .hex_value(hex_value), .com(com), .seg_7(seg_7));
endmodule

// BCD 60진 Down Counter
module bcd_60_down_counter (
    input clk, reset_p,
    input clk_source,
    input start_set,
    input [3:0] loadable_bcd10, loadable_bcd1,
    output reg sec_zero_signal,
    output reg [3:0] bcd10, bcd1 );
    
    wire clk_source_nedge;
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_source_nedge));
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin bcd10 = 0; bcd1 = 0; sec_zero_signal = 0;end
        else begin
            if(!start_set) 
                begin bcd10 = loadable_bcd10; bcd1 = loadable_bcd1; end
            else if(clk_source_nedge) begin
                if(bcd1 == 0) begin
                    bcd1 = 9;
                
                    if(bcd10 == 0) 
                        begin bcd10 = 5; sec_zero_signal = 1; end
                    else 
                        begin bcd10 = bcd10 - 1; end 
                 end
                 else begin bcd1 = bcd1 - 1; end
            end 
            else sec_zero_signal = 0;
        end
    end   
endmodule

// BCD 60진 Up Counter
module bcd_60_up_counter (
    input clk, reset_p,
    input btn,
    input start_set,
    output reg [3:0] bcd10, bcd1 );
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin bcd10 = 0; bcd1 = 0; end
        else if(btn && !start_set) begin
            if(bcd1 >= 9) begin
                bcd1 = 0;
                
                if(bcd10 >= 5) bcd10 = 0;
                else bcd10 = bcd10 + 1;
            end
            else bcd1 = bcd1 + 1;
        end
    end   
endmodule

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

// Button Control
module button_cntr (
    input clk, reset_p,
    input btn,
    output btn_pedge, btn_nedge );
    
    // Get One Cycle Puls of 1msec.
    wire clk_1usec, clk_1msec;
    clk_div_100 clk_div_1usec (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    
    reg out_button;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) out_button = 0;
        else if(clk_1msec) out_button = btn;
    end
    
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(out_button), .n_edge(btn_nedge), .p_edge(btn_pedge));
    
endmodule

// Clock Divider 60
module clk_div_60 (
    input clk, reset_p,
    input clk_source,
    output clk_div_60,
    output clk_div_60_nedge, clk_div_60_pedge );
    
    wire clk_source_nedge;
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_source_nedge));
    
    reg [5:0] count;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) count = 0;
        else if(clk_source_nedge) begin
            if(count >= 59) count = 0;
            else count = count + 1;
        end
    end
    
    assign clk_div_60 = (count < 30) ? 0 : 1;
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_60), .n_edge(clk_div_60_nedge), .p_edge(clk_div_60_pedge));
    
endmodule

// Clock Divider 1000
module clk_div_1000 (
    input clk, reset_p, 
    input clk_source,
    output clk_div_1000,
    output clk_div_1000_nedge, clk_div_1000_pedge );
    
    wire clk_source_nedge;
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_source_nedge));
    
    reg[9:0] count;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) count = 0;
        else if(clk_source_nedge) begin
            if(count >= 999) count = 0;
            else count = count + 1;
        end
    end
    
    assign clk_div_n = (count < 500)? 0 : 1;
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_n), .n_edge(clk_div_1000_nedge), .p_edge(clk_div_1000_pedge));
endmodule

// Clock Divider 100
module clk_div_100 (
    input clk, reset_p,
    output clk_div_100, 
    output clk_div_100_nedge, clk_div_100_pedge );
    
    reg [6:0] count;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) count = 0;
        else begin
            if(count >= 99) count = 0;
            else count = count + 1;
        end
    end
    
    assign clk_div_100 = (count < 50)? 0 : 1;
    
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_div_100), .n_edge(clk_div_100_nedge), .p_edge(clk_div_100_pedge));
endmodule

// FND Control
module fnd_cntr (
    input clk, reset_p,
    input [15:0] hex_value,
    output [3:0] com,
    output [7:0] seg_7 );
    
    ring_counter ring_counter_com (.clk(clk), .reset_p(reset_p), .com(com));
    
    reg [3:0] value;
    always @(*) begin
        case (com) 
            4'b1110 : value = hex_value [3:0];
            4'b1101 : value = hex_value [7:4];
            4'b1011 : value = hex_value [11:8];
            4'b0111 : value = hex_value [15:12];
            default : value = value;
        endcase
    end
    
    decoder_seg7 decoder_7_segment (.hex_value(value), .seg_7(seg_7));
endmodule

// Ring Counter of Com value.
module ring_counter (
    input clk, reset_p,
    output reg [3:0] com );
 
    // Get One Cycle Pulse of 1msec.
    wire clk_1usec, clk_1msec;
    clk_div_100 clk_div_1usec (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) com = 4'b1110;
        else if(clk_1msec) com = {com[2:0], com[3]};
    end
endmodule

// Decoder of 7-segment.
module decoder_seg7 (
    input [3:0] hex_value,
    output reg [7:0] seg_7 );
    
    always @(*) 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 (
    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
        else 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