거북이처럼 천천히

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

RTL Design/Verilog RTL 설계

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

유로 청년 2024. 8. 8. 21:44

1. Cooking Timer 

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

 

 

2. State diagram 

  • Cooking Timer는 2가지 모드를 갖는다.
    - Start mode : Cooking Timer가 세팅된 시간부터 1초씩 Down Counting하는 모드
    - Set mode : Cooking Timer의 시작 시간을 세팅하는 모드
  • btn[0]를 btn_start_set 버튼으로 지정하여 btn[0]에 의해 모드 전환이 발생하며, 이를 상태도로 표현하면 다음과 같다.

State diagram of Cooking Timer

  • 이외에도 btn[1] ~ btn[3] 버튼을 각각 다음과 같은 이름 및 역활을 부여하였다.
    - btn[1] : Set mode에서 분 값을 조절하는 버튼
    - btn[2] : Set mode에서 초 값을 조절하는 버튼
    - btn[3] : Buzz가 울릴 경우, Buzz을 끄는 버튼

 

 

3. Cooking Timer의 기본 틀 생성


3.1. Cooking Timer의 동작

- Cooking Timer는 2가지 모드(Start mode, Set mode)를 가지며, start_set 변수를 통해 현재의 모드를 결정한다.
- start_set 변수 값이 1이면 Start mode이며, 변수 값이 0이면 Set mode임을 의미한다.
- Set mode 일 경우, btn[1]과 btn[2]을 통해 Cooking Timer의 초기 세팅 시간 값을 조절할 수 있으며,
   Start mode 일 경우, 초기 세팅된 시간을 시작으로 1초씩 Down Counting하게 된다.


3.2. Cooking Timer에서는 어떤 Counter들이 필요한가?

- Set mode에서 btn[1]과 btn[2]에 의해 Cooking Timer의 초기 세팅 시간 값을 Up Counting해서 조절해야 하기
  때문에 BCD 60진 up Counter가 필요할 것이다.
- Start mode에서는 초기 세팅 시간을 시작으로 1초씩 down counting 해야 하기 때문에 BCD 60진 down Counter가 
  필요할 것이다.

 

 

3.3. Cooking Timer의 기본 틀에 대한 소스 코드

- 소스 코드와 함께 Cooking Timer의 동작에 대해 설명하고자 한다.
- button control, clock divider, FND Control 등의 모듈은 이전 게시글에서 사용하고, 설명하였기 때문에 이에 대한
  자세한 설명은 생략하고, 추가된 모듈을 중점으로 설명을 하도록 하겠다.
- button control, clock divdier, FND Control에 대해서 궁금하다면 아래 게시글을 참고 하길 바란다.
  https://jbhdeve.tistory.com/273

 

Verilog RTL 설계(7월 18일 - 5, Stop Watch - 4)

1. 4초 때 리셋 버튼을 누른 뒤, 다시  Stop watch를 실행하니 56초때 분값이 증가한다.  1.1. 문제 현상4초 때 리셋 버튼을 누른 뒤, 다시 Stop Watch를 실행하니 56초때 분값이 증가하는 현상을 확인할

jbhdeve.tistory.com

 

 

 

< Top Module of Cooking Timer의 변수 선언 >

module Cooking_Timer_0(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output [1:0] mode_led );
    
    // Mode led
    assign mode_led [1:0] = (start_set)? 2'b10 : 2'b01;
    
endmodule
  • mode_led는 현재 모드 상태를 LED로 표현하기 위한 변수이다.
  • 첫 번째 LED는 set 모드, 두 번째 LED는 start 모드임을 나타낸다.

 

 

 

< 주기가 1초인 One Cycle Pulse 얻기 >

// 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));
  • Set mode에서 1초마다 Down Counting할 것이기 때문에 주기가 1초인 One Cycle Pulse가 필요하다.
  • Q) Stop Watch, Normal Watch에서 사용했던 주기가 1분인 One Cycle Pulse는 필요하지 않는가?
  • A) Cooking Timer의 분 값이 변화하는 시점은 초 값이 0초 되었을 때이기 때문에 초 값이 0초가 되었음을
         알려주는 One Cycle Pulse가 필요하지 1분인 One Cycle Pulse는 필요하지 않다.

 

 

 

< Chattering 문제를 방지하기 위해 Button Control 모듈을 통해 버튼의 One Cycle Pulse 얻는다. >

// Get One Cycle Pulse of Button.
wire btn_start_set, btn_min, btn_sec;
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 모듈은 D-Flip Flop을 이용하여 1msec delay time을 갖고, Button 값을 읽는 모듈이다.
  • button 값을 1msec 동안의 delay time을 갖고, 버튼 값을 읽기 때문에 1msec 동안 발생한 불안정한 버튼 값을 무시 및 읽지 않고, 안정된 버튼 값을 읽음으로서 버튼 Chattering문제를 해결할 수 있다.

 

 

 

 

< Set 모드일 때, 버튼을 통해 분, 초 값을 조정할 수 있다. >

 // 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진 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
  • bcd_60_up_counter 모듈은 아래 조건을 만족했을 경우에만 1씩 Up Counting을 하게 된다.
    - 분 or 초 버튼이 눌렀을 경우
    - start_set 변수 값이 0일 경우
  • 분 or 초 버튼이 누를 경우, 버튼 값이 0 → 1 로 변화하면서 Positive edge 발생시키게 된다.
  • set mode에서 분 or 초 버튼을 통해 시간을 세팅하기 때문에 set mode에서만 Up Counter가 동작하도록 하기 위해서 start_set 값이 0인 경우에만 동작하도록 설계한다.

 

 

 

< Start 모드일 때, 1초씩 Down Counting을 하기 위해 Loadable down counter를 설계한다. >

// 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));
// 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 );
    
    // Get One Cycle Pulse of clk_source
    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 // Reset BCD10, BCD1
        else begin
            if(!start_set)   // Load BCD10, BCD1 of Set mode
                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
  • Set mode에서 Start mode로 전환되어 1초씩 Down Counting하기 위해서는 "Set 모드에서 몇 분 몇 초로 세팅되었는가?"에 대해서 알 필요가 있으며, 이를 위해 "Loadable BCD 60진 Counter"를 만들었다.
  • Loadable BCD 60진 Down Counter는 start_set 값이 0 → 1 로 변화할 때, BCD 60진 Up Counter로 부터 세팅된 BCD10, BCD1 값을 받고, 이를 시작점으로 1초씩 Down Counting을 한다.
  • 이를 위해 start_set 값이 0인 (= Set mode인 상태) 동안에는 계속해서 Up Counter로부터 BCD10, BCD1 값을 받는다.
  • start_set값이 1인 (= Start mode인 상태) 동안에는 주기가 1초인 One Cycle Pulse가 활성화된 상태에서만 1초씩 다운 카운팅을 하게 된다.


    ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
  • Q) 분 BCD 60진 Down Counter는 왜 clock Source를 clk_1min이 아닌 초 BCD 60진 Down Counter에서 0초일 때마다 발생하는 One Cycle Pulse를 받는가?
  • A) "언제 분 값이 1씩 감소해야 하는가?"에 대한 질문을 생각해 볼 필요가 있다. 이전에 다루었던 Stop Watch, Noraml Clock는 초 값이 60초 일 때, 1분씩 증가했다. 그러나, Cooking Timer는 반대로 초 값이 0초일때마다 1분씩 감소한다. 이는 분 값이 감소하는 것은 1분마다 감소하는 것이 아닌 랜덤으로 발생하는 초 값이 0초 일때마다 감소하는 것이기 때문에 초 BCD 60진 Down Counter에서 0초일 때마다 발생하는 One Cycle Pulse (sec_zero_signal) 가 활성화 되었는가를 기준으로 삼을 필요가 있다.


  • Q) 이를 위해서 초 BCD 60진 Down Counter에서만 sec_zero_signal wire를 사용하는가?
  • A) 그렇다. 분 BCD 60진 Down Counter에게 "0초입니다. 분 값을 1분 줄여주세요." 라고 알려줄 신호가 필요하기 때문에 이를 위해 sec_zero_signal 를 1초 동안만 활성화 시킴으로서 분 BCD 60진 Down Counter는 이를 바탕으로 1분씩 Down Counting을 실시한다.

 

 

 

 

< Set, Start 모드의 시간 값을 결합한 뒤, start_set 변수 값에 의해 현재 모드에 맞는 값을 선택한다. >

// 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;
  • 결합 연산자를 통해 분과 초의 BCD10, BCD1를 결합한다.
  • 그러고 난 뒤. start_set 변수 값을 통해 현재 모드에 알맞는 시간 값을 선택하여 hex_value 와이어와 연결한다.

 

 

 

< 모드 변환 버튼을 누를 때마다 모드 변환하며, Cooking Timer가 0분 0초에 도달하면 모드 값을 초기화 한다. >

 // Start_set mode 선언 및 0분 0초가 되면 Reset하기.
    reg start_set;
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) start_set = 0;
        else if(hex_value_cur == 16'b0) begin
            start_set = 0;
        end
        else if(btn_start_set) start_set = ~ start_set;
    end
  • 모드 변환 버튼인 btn_start_set 을 누를 때마다 start_set 값을 Toggle시켜 모드를 변환하게 된다.
  • Cooking Timer가 0분 0초에 도달하면 start_set 값은 0으로 초기화되며, Down Counting은 멈추고, 다시 Setting mode로 전환된다.

 

 

 

< 현재 모드에 맞게 선택된 시간을 FND로 출력한다. >

// 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));

 

 

 

 

 

 

4. 구현 영상 

 

 

 

 

5. 전체 소스 코드

// Top module of Cooking Timer
module Cooking_Timer_0(
    input clk, reset_p,
    input [2:0] btn,
    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] = clk_1sec;
    
    // 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;
    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));
    
    // 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) start_set = 0;
        else if(hex_value_cur == 16'b0) begin
            start_set = 0;
        end
        else if(btn_start_set) start_set = ~ start_set;
    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