Notice
Recent Posts
Recent Comments
Link
관리 메뉴

거북이처럼 천천히

Verilog RTL 설계(7월 22일 - 3, 4X4 Matrix Keyboard - 3) 본문

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 22일 - 3, 4X4 Matrix Keyboard - 3)

유로 청년 2024. 7. 25. 22:18

1. Button을 짧은 시간동안 연속적으로 누를 경우, 이전 값이 잠깐 출력되는 문제점이 발생

1.1. 문제 현상

  • 버튼을 짧은 시간동안 연속적으로 누르면 FND에서 이전 값이 잠깐 출력되는 문제점이 발생한다.

 

 

1.2. 문제 원인

  • 아래 코드와 함께 이해하고자 한다면 쉽게 이해할 수 있다.
// state에서 next_state로 넘어가는 시점은 clk_10msec_n 일때, 넘어갔다.
always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = SCAN0;
        else if(clk_10msec_n) state = next_state;
end


''' (중간 생략) '''


// Row값과 Col값을 비교하여 입력된 키 값을 찾아 Key_value 변수에 대입하는 시점도
// clk_10msec_n 일때, 발생하였다.
always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            key_value = 0;
            key_valid = 0;
            col = 0;
        end
        else if(clk_10msec_n) begin
             case(state) 
             
             endcase
        end
end
  • 위 코드를 보면 알 수 있듯이 next_state로 넘아가는 시점과 key 값을 찾아 key_value 변수에 대입하는 시점 모두 clk_10msec_n에서 발생하도록 설계하였다.
  • 만약, 위와 같이 설계된 상태에서 state가 변화하게 된다면, state 변수는 PDT를 갖고, 변화하기 때문에 key 값을 찾는 과정에서 row 값을 이용할 때, 변화된 row 값을 사용하는 것이 아닌 변화되기 이전의 row 값을 사용하게 된다.
  • 따라서 이 과정에서 row 값이 안정화되지 않은 상태 이기 때문에 잘못된 키의 값과 출력을 발생시키게 되는 것이다.

 

 

1.3. 문제 해결 방법

  • PDT를 고려하여 state 값이 변화하는데, 충분한 시간을 줄 필요가 있다.
  • 따라서 state 값이 충분히 안정화될 시간을 주기 위해서 서로 다른 edge에서 동작하도록 설계할 필요가 있다.
  • 따라서 next_state로 넘어가는 시점이 "Negative edge"라면 key_value 값을 찾는 시점을 "Positive edge"로 설정해야하며, 반대로 next_state로 넘어가는 시점이 "Positive edge"라면 key_value 값을 찾는 시점을 "Negative edge"로 설정해야 한다.

 

 

1.4. 문제 수정 코드

  • next state로 넘어가는 시점이 clk_10msec_n라고 설정하였을 경우, 아래와 같이 key_value 값을 찾는 과정은 clk_10msec_p에 찾도록 설계하면 된다.

// state에서 next_state로 넘어가는 시점은 clk_10msec_n 일때, 넘어갔다.
always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = SCAN0;
        else if(clk_10msec_n) state = next_state;
end


''' (중간 생략) '''


// Row값과 Col값을 비교하여 입력된 키 값을 찾아 Key_value 변수에 대입하는 시점은
// clk_10msec_p 일때, 발생하였다.
always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            key_value = 0;
            key_valid = 0;
            col = 0;
        end
        else if(clk_10msec_n) begin
             case(state) 
             
             endcase
        end
end

 

 

 

1.5. 수정된 전체 코드

module keyPad_test (
    input clk, reset_p,
    input [3:0] col,
    output [3:0] com,
    output [7:0] seg_7,
    output [3:0] row );
    
    wire [3:0] key_value;
    wire key_valid;
    KeyPad keypad_module (.clk(clk), .reset_p(reset_p), .col(col), .row(row), .key_value(key_value), .key_valid(key_valid));
    
     fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value({12'b0, key_value}), .com(com), .seg_7(seg_7));
endmodule

module KeyPad(
    input clk, reset_p,
    input [3:0] col,
    output reg [3:0] row,
    output reg [3:0] key_value,
    output reg key_valid );
    
    // State 선언
    parameter SCAN0 = 5'b00001;
    parameter SCAN1 = 5'b00010;
    parameter SCAN2 = 5'b00100;
    parameter SCAN3 = 5'b01000;
    parameter KEY_PROCESS = 5'b10000;
    
    // 10ms One Cycle Pulse 얻기 
    reg [19:0] count;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) count = 0;
        else count = count + 1;
    end
    
    wire clk_10ms_pedge, clk_10ms_nedge;
    edge_detector edge_detecot_0(.clk(clk), .reset_p(reset_p), .cp(count[19]), .p_edge(clk_10ms_pedge), .n_edge(clk_10ms_nedge));
    
    // state, next_state 변수 선언
    reg [4:0] state, next_state;

    // 언제 다음 state로 넘아가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            state = SCAN0;
        end else if(clk_10ms_pedge) state = next_state;    
    end
    
    // 발생한 이벤트에 따라 다음 state를 결정한다.
    always @(*) begin
             case(state)
                SCAN0 : begin
                    if(col == 0) next_state =  SCAN1;
                    else next_state = KEY_PROCESS;
                end
                
                SCAN1 : begin
                    if(col == 0) next_state =  SCAN2;
                    else next_state = KEY_PROCESS;
                end
                
                
                SCAN2 : begin
                    if(col == 0) next_state =  SCAN3;
                    else next_state = KEY_PROCESS;
                end
                
                
                SCAN3 : begin
                    if(col == 0) next_state =  SCAN0;
                    else next_state = KEY_PROCESS;
                end
                
                KEY_PROCESS : begin
                    if(col == 0) next_state =  SCAN0;
                    else next_state = KEY_PROCESS;
                end    
                
                default : next_state = SCAN0;    
             endcase
    end
    
    
    // 각 state에서의 동작 정의
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            key_value = 0;
            key_valid = 0;
            row = 0;
        end 
        else if(clk_10ms_nedge) begin
            case(state) 
                SCAN0 : begin
                    row = 4'b0001;
                    key_valid = 0;
                end
                
                SCAN1 : begin
                    row = 4'b0010;
                    key_valid = 0;
                end
                
                SCAN2 : begin
                    row = 4'b0100;
                    key_valid = 0;
                end
                
                SCAN3 : begin
                    row = 4'b1000;
                    key_valid = 0;
                end
                
                KEY_PROCESS : begin
                    key_valid = 1;
                    
                    case({row, col})
                        8'b0001_0001 : key_value = 16'h0;
                        8'b0001_0010 : key_value = 16'h1;
                        8'b0001_0100 : key_value = 16'h2;
                        8'b0001_1000 : key_value = 16'h3;
                        
                        8'b0010_0001 : key_value = 16'h4;
                        8'b0010_0010 : key_value = 16'h5;
                        8'b0010_0100 : key_value = 16'h6;
                        8'b0010_1000 : key_value = 16'h7;
                        
                        8'b0100_0001 : key_value = 16'h8;
                        8'b0100_0010 : key_value = 16'h9;
                        8'b0100_0100 : key_value = 16'ha;
                        8'b0100_1000 : key_value = 16'hb;
                        
                        8'b1000_0001 : key_value = 16'hc;
                        8'b1000_0010 : key_value = 16'hd;
                        8'b1000_0100 : key_value = 16'he;
                        8'b1000_1000 : key_value = 16'hf;
                        
                        default : key_value = key_value;
                    endcase
                end
            endcase
        end
    end
    
endmodule

// FNC Control
module fnd_cntr (
    input clk, reset_p,
    input [15:0] value,
    output reg [3:0] com,
    output [7:0] seg_7);
    
    // Get 1ms One Cycle Pulse
    wire clk_1ms;
    reg [17:0] count;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) count = 0;
        else count = count + 1;
    end
    
    edge_detector edge_detecot_0(.clk(clk), .reset_p(reset_p), .cp(count[17]), .n_edge(clk_1ms));
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) com = 4'b1110;
        else if(clk_1ms) 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
    
    decoder_seg7 decoder_fnd(.hex_value(hex_value), .seg_7(seg_7));
endmodule

// Decoder 7-segment.
module decoder_seg7 (
    input [3:0] hex_value,
    output reg [7:0] seg_7 );
    
    always @(hex_value) begin
        case(hex_value)
            0  : seg_7 = 8'b0000_0011;
            1  : seg_7 = 8'b1001_1111;
            2  : seg_7 = 8'b0010_0101;
            3  : seg_7 = 8'b0000_1101;
            4  : seg_7 = 8'b1001_1001;
            5  : seg_7 = 8'b0100_1001;
            6  : seg_7 = 8'b0100_0001;
            7  : seg_7 = 8'b0001_1111;
            8  : seg_7 = 8'b0000_0001;
            9  : seg_7 = 8'b0000_1001;
            10 : seg_7 = 8'b0001_0001;
            11 : seg_7 = 8'b1100_0001;
            12 : seg_7 = 8'b0110_0011;
            13 : seg_7 = 8'b1000_0101;
            14 : seg_7 = 8'b0110_0001;
            15 : seg_7 = 8'b0111_0001;
        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