본문 바로가기

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 18일 - 1, Advanced Clock Mode - 4)

1. 초 값이 30초 이상일 때, btn_set 버튼을 누를 때마다 분 값이 1씩 증가한다. (또 다른 해결책)

 

Verilog RTL 설계(7월 17일 - 5, Advanced Clock Mode - 2)

1. Set 모드에서 시간을 설정한 뒤, 다시 Watch 모드로 가면 초와 분이 동기화되지 않는다. 1.1 문제 현상Set 모드에서 시간을 세팅한 뒤, 다시 Watch 모드로 돌아가 시계로서 동작하면 초와 분 값이 동

jbhdeve.tistory.com

 

해당 문제의 원인은 아래 코드가 원인이 되었다.

assign inc_min = (set_watch)? btn_min : clk_min;

 

  • clk_min은 주기가 1min이며, Duty Cycle이 50%인 Pulse wave이다.
  • 따라서 0 ~ 29초 동안에는 Low 전압 레벨을 갖지만, 30 ~ 59초동안에는 High 전압 레벨을 갖는다.
  • 만약 30초 이상인 상태에서는 clk_min 값은 1 값을 가질 것이며, minute button은 아직 누르지 않은  상태라면 0 값을 가질 것이다.
  • 이 상태에서 Set mode button을 눌러 set_watch 모드를 변경한다면 inc_min 변수의 값은 1 → 0으로 변화가 발생 할 것이며, 이는 Negative edge를 발생시킬 것이다. 

 

bcd_60_counter counter_min(.clk(clk), .reset_p(reset_p), .clk_source(inc_min), .bcd1(min_1), .bcd10(min_10));
  • 이로 인해 BCD 60진 카운터에서는 inc_min 펄스파를 기준으로 카운팅하기 때문에 inc_min 펄스파가 활성화되어 분 값이 1씩 증가할 것이다.

 

 

 

 

1.2. 문제의 해결책

  • 문제는 clk_min 값이 1인 경우에 발생했기 때문에 clk_min이 활성화된 시간을 최소화함으로서 해결하고자 했다.
  • 따라서 Duty cycle이 50%인 Pulse wave가 아닌 10ns 동안에만 활성화된 One Cycle Pulse를 활용하여 문제를 해결 하였다.
clk_div_n clk_div_min(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .prescale(60), .clk_div_n_nedge(clk_min));

 

 

 

 

 

 

1.3. 또 다른 해결책

  • 하지만, 10ns동안 활성화된 One Cycle Pulse를 이용한다 하더라도 정말 낮은 확률로 10ns동안 활성화된 시점에 Watch mode에서 Set mode로 변경한다면 동일한 문제가 이어질 것이다.
  • 따라서 이를 해결하기 위해 다른 방법을 사용하고자 한다.
  • 또 다른 해결 방법은 Watch 모드의 BCD 60진 Counter와 Set 모드의 BCD 60진 Counter로 분리하여 서로 각각 Counting하는 것이다. 

 

 

1.3.1. BCD 60진 Counter를 분리한다는 의미는 무엇인가? 

  • BCD 60진 Counter를 분리한 논리 회로는 다음과 같다.

Watch의 BCD 60진 Counter와 Set의 BCD 60진 Counter를 분리하여 Counting한뒤, DEMUX를 통해 set_watch 변수 값에 따라 선택하여 출력한다.

 

  • 이전에서는 Watch 모드와 Set 모드는 하나의 BCD 60진 Counter를 사용했다.
  • Watch 모드와 Set 모드의 BCD 60진 Counter를 분리하여 구현함으로서 Watch 모드에서는 Watch모드의 60진 BCD 카운터로 분과 초를 counting하며, Set 모드에서는 Set모드의 60진 BCD 카운터로 분과 초를 counting한다.
  • 이렇게 계산된 분과 초의 데이터를 결합 연산자를 통해 16bit 데이터로 결합한 뒤, DEMUX를 통해 현재 모드(set_watch)에 따라 해당 모드의 맞는 분과 초를 출력한다.
  • 이렇게 Set mode와 Watch 모드의 BCD 60진 카운터를 분리함으로서 모드 변함으로 인한 분과 초의 Counting 방법 변환 및 분과 초의 데이터과 독립적으로 동작하기 때문에 초 값이 30초 이상일 때, btn_set 버튼을 누를 때마다 분 값이 1씩 증가하는 문제점을 해결할 수 있다.

 

 

 

 

1.3.2 이를 어떻게 코드로 구현하는가?

  • 이에 대해서는 코드와 함께 설명하도록 하겠다.
// Loadalbe BCD 60 Counter.
module loadable_bcd_60_counter (
    input clk, reset_p,
    input clk_time,
    input load_enable,
    input [3:0] load_bcd1, load_bcd10,
    output reg [3:0] bcd1, bcd10 );
    
    // Get One Cycle Pulse of clk_time
    wire clk_time_nedge;
    edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_time), .n_edge(clk_time_nedge));
    
    // Counting
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            bcd1 = 0;
            bcd10 = 0;
        end
        else if(load_enable) begin
            bcd1 = load_bcd1;
            bcd10 = load_bcd10;
        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

 

  • 기본 형태는 이전에 구현했던 bcd_60_counter 모듈과 유사하다.
  • 다만, Watch 모드와 Set 모드의 Counter가 분리되었기 때문에 서로의 Counter가 어디까지 Counting했는지, 서로의 Counter는 현재 몇 분, 몇 초에 있는지 모르는 상태이다.
  • 따라서 모드가 변했을 때, 해당 모드의 Counter는 이전 모드의 Counter로 부터 몇분 몇초까지 Counting했는지 여부를 알 필요가 있다. 그래야 이전 모드의 Counter가 Counting했던 분과 초를 시작으로 계속해서 Counting을 이어 나갈 수 있다.
  • 이를 위해서 load_enable 변수와 load_bcd1, load_bcd10 변수를 입력 값으로 받는다.
    - load_enable : 모드가 변화하여 이전 모드의 Counter로 부터 이전까지 Counting한 분과 초를 받을지 여부를 결정하는 변수
    - load_bcd1 : 이전 모드의 Counter가 Counting했던 분 / 초의 일의 자리
    - load_bcd10 : 이전 모드의 Counter가 Counting했던 분 / 초의 십의 자리
  • 위 변수 값을  통해 모드가 변화하였을 때, 현재 모드의 Counter는 이전 모드의 Counter로부터 분과 초를 받을 지 여부를 결정하고, 이전 모드의 Counter로부터 데이터를 이어 받아 계속해서 Counting하게 된다.

 

 

 

 

1.3.3. 그럼 언제 각각의 Counter들은 이전 모드의 Counter로 부터 데이터를 이어 받는가?

  • 각각의 Counter들은 이전 모드의 Counter로 부터 데이터를 이어 받는 시점은 set_watch 변수 값이 변화하였을 시에 이어 받는다.
  • set_watch 값이 0 → 1로 변화하게 되면
    - watch 모드에서 set 모드로 변화
    - set_watch 변수에서 Positive edge 발생
    - 즉, set_watch 변수에서 Positive edge가 발생하면 set 모드의 Counter는 watch 모드의 Counter로 부터 데이터를 이어받는다.
  • set_watch 값이 1 → 0로 변화하게 되면
    - set 모드에서 watch 모드로 변화
    - set_watch 변수에서 Negative edge 발생
    - 즉, set_watch 변수에서 Negative edge 가 발생하면 watch 모드의 Counter는 set 모드의 Counter로 부터 데이터를 이어받는다.

  • 즉, set_watch 변수에서 Positive edge 및 Negative edge가 발생했는지 여부에 따라 각각의 Counter들은 다른 Counter로 부터 데이터를 이전 받아 계속해서 Counting을 이어간다.
 // Edge detector of set_watch.
wire watch_load_en, set_load_en;
edge_detector_n get_watch_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .n_edge(watch_load_en));
edge_detector_n get_set_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .p_edge(set_load_en));

 

 

 

 

1.3.4. 서로 다른 모드들의 BCD 60진 counter 정의

  • Watch 모드와 Set 모드의 BCD 60진 counter가 독립적이다.
  • 따라서 Watch 모드와 Set 모드의 BCD 60진 counter를 각각 분과 초에 대해서 정의할 필요가 있다.
  • 또한 각각의 Counter들이 이전 모드의 Counter로 부터 데이터를 이전 받아 Counting하는 것은 set_watch 변수에서 Positive edge 및 Negative edge가 발생했는지 여부에 따라 동작하게 된다.
// BCD 60 counter of Watch mode
loadable_bcd_60_counter sec_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(watch_load_en), .load_bcd1(set_sec1), .load_bcd10(set_sec10), .bcd1(watch_sec1), .bcd10(watch_sec10));
loadable_bcd_60_counter min_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(watch_load_en), .load_bcd1(set_min1), .load_bcd10(set_min10), .bcd1(watch_min1), .bcd10(watch_min10));
    
// BCD 60 counter of Set mode
loadable_bcd_60_counter sec_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(set_load_en), .load_bcd1(watch_sec1), .load_bcd10(watch_sec10), .bcd1(set_sec1), .bcd10(set_sec10));
loadable_bcd_60_counter min_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(set_load_en), .load_bcd1(watch_min1), .load_bcd10(watch_min10), .bcd1(set_min1), .bcd10(set_min10));

 

 

 

 

 

1.3.5. 모드에 따라 두 개의 데이터 중 현재 모드에 따라 결정하여 출력한다.

  • Watch 모드의 counter로 부터 얻은 데이터와 Set 모드의 counter로 부터 얻은 데이터를 결합 연산자를 이용하여 각각 결합한다.
  • 그리고, 현재 모드(set_watch) 값에 따라 결정하여 해당 값들을 출력하게 된다.
// Combine data of second, minute. 
wire [15:0] watch_value, set_value, hex_value;
assign watch_value = {watch_min10, watch_min1, watch_sec10, watch_sec1};
assign set_value = {set_min10, set_min1, set_sec10, set_sec1};
    
// Choose data of minute, second by using set_watch.
assign hex_value = (set_watch)?  set_value : watch_value;
    
// Print the FND
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(hex_value), .com(com), .seg_7(seg_7));

 

 

 

 

1.3.6. 구현 영상

 

 

 

 

 

1.3.7. 전체 소스 코드

// Root of module
module Set_Clock_Mode(
    input clk, reset_p,
    input [3:0] btn,
    output [3:0] com,
    output [7:0] seg_7 );
    
    // Get pulse wave of sec, min
    wire clk_usec, clk_sec, clk_min;
    clk_div_100 clk_div_usec (.clk(clk), .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(inc_sec), .prescale(60), .clk_div_n_nedge(clk_min));
    
    // Get One Cycle Pulse of button.
    wire btn_set, btn_sec, btn_min;
    button_cntr edge_btn_set (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_nedge(btn_set));
    button_cntr edge_btn_sec (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_nedge(btn_sec));
    button_cntr edge_btn_min (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_nedge(btn_min));
    
    // select mode.
    wire set_watch;
    t_flip_flop t_ff (.clk(clk), .reset_p(reset_p), .t(btn_set), .q(set_watch));
    
    // Select standard pulse of bcd 60 counter.
    // when current state is watch mode, standard pulse is clk_sec, clk_min
    // when current state is set mode, standard pulse is btn_sec, btn_min
    wire inc_sec, inc_min;
    assign inc_sec = (set_watch)? btn_sec : clk_sec;
    assign inc_min = (set_watch)? btn_min : clk_min;
    
    // Variable of Watch mode, Set mode
    wire [3:0] watch_sec10, watch_sec1, watch_min10, watch_min1; 
    wire [3:0] set_sec10, set_sec1, set_min10, set_min1; 
    
    // Edge detector of set_watch.
    wire watch_load_en, set_load_en;
    edge_detector_n get_watch_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .n_edge(watch_load_en));
    edge_detector_n get_set_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .p_edge(set_load_en)); 
    
    // BCD 60 counter of Watch mode
    loadable_bcd_60_counter sec_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(watch_load_en), .load_bcd1(set_sec1), .load_bcd10(set_sec10), .bcd1(watch_sec1), .bcd10(watch_sec10));
    loadable_bcd_60_counter min_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(watch_load_en), .load_bcd1(set_min1), .load_bcd10(set_min10), .bcd1(watch_min1), .bcd10(watch_min10));
    
    // BCD 60 counter of Set mode
    loadable_bcd_60_counter sec_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(set_load_en), .load_bcd1(watch_sec1), .load_bcd10(watch_sec10), .bcd1(set_sec1), .bcd10(set_sec10));
    loadable_bcd_60_counter min_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(set_load_en), .load_bcd1(watch_min1), .load_bcd10(watch_min10), .bcd1(set_min1), .bcd10(set_min10));
    
    // Combine data of second, minute. 
    wire [15:0] watch_value, set_value, hex_value;
    assign watch_value = {watch_min10, watch_min1, watch_sec10, watch_sec1};
    assign set_value = {set_min10, set_min1, set_sec10, set_sec1};
    
    // Choose data of minute, second by using set_watch.
    assign hex_value = (set_watch)?  set_value : watch_value;
    
    // Print the FND
    fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(hex_value), .com(com), .seg_7(seg_7));
endmodule

// Control Button.
module button_cntr (
    input clk, reset_p,
    input btn,
    output btn_pedge, btn_nedge);
    
    // Get 1ms Pulse wave, 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_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_1ms), .n_edge(clk_1ms_nedge));
    
    // Get positive edge of button, Negative edge of 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_detector_1 (.clk(clk), .reset_p(reset_p), .cp(debounced_btn), .p_edge(btn_pedge), .n_edge(btn_nedge)); 
endmodule

// T Flo 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 if(t) q = ~q;
        else q = q;
    end
endmodule

// Loadalbe BCD 60 Counter.
module loadable_bcd_60_counter (
    input clk, reset_p,
    input clk_time,
    input load_enable,
    input [3:0] load_bcd1, load_bcd10,
    output reg [3:0] bcd1, bcd10 );
    
    // Get One Cycle Pulse of clk_time
    wire clk_time_nedge;
    edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_time), .n_edge(clk_time_nedge));
    
    // Counting
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            bcd1 = 0;
            bcd10 = 0;
        end
        else if(load_enable) begin
            bcd1 = load_bcd1;
            bcd10 = load_bcd10;
        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

// BCD 60 Counter
module bcd_60_counter (
    input clk, reset_p,
    input clk_source,
    output reg [3:0] bcd1, bcd10 );

    wire clk_sourc_nedge;
    edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_sourc_nedge));
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            bcd1 = 0;
            bcd10 = 0;
        end else if(clk_sourc_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

// prescale 100.
module clk_div_100 (
    input clk, reset_p,
    output clk_div_100,
    output 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_detector (.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);
    
    // Shifting the com value every 1ms.
    // prescale 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
    
    // period 1ms Pulse wave
    wire clk_div_100000, clk_div_100000_nedge;
    assign clk_div_100000 = (counter < 50000) ? 0 : 1;
    
    // period 1ms One Cycle Pulse
    edge_detector_n edge_detector_0(.clk(clk), .reset_p(reset_p), .cp(clk_div_100000), .n_edge(clk_div_100000_nedge));
    
    // Shifting com value
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) com = 4'b1110;
        else if(clk_div_100000_nedge) begin
            if(com == 4'b0111) com = 4'b1110;
            else com = {com[2:0], 1'b1}; 
        end
    end
    
    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
    
    // Get fnd data
    decoder_seg_7 fnd (.hex_value(hex_value), .seg_7(seg_7));
endmodule

// Decoder of 7-seg
module decoder_seg_7 (
    input [3:0] hex_value,
    output reg [7:0] seg_7 );
    
    always @(hex_value) begin
        case(hex_value)
            0  : seg_7 = 8'b0000_0011;  // 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'b0000_0101;  // 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 p_edge, n_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