Notice
Recent Posts
Recent Comments
Link
관리 메뉴

거북이처럼 천천히

4X4 Matrix KeyPad - 2 본문

RTL Design/Verilog 연습

4X4 Matrix KeyPad - 2

유로 청년 2024. 8. 10. 00:04

1. 4X4 Matrix KeyPad (FSM 형식)

  • 이번에는 FSM (= Finite State Machine) 형식으로 KeyPad를 구현해보도록 하겠다.
  • 아래와 같이 5가지 상태로 정의하였다.
    - S_SCAN0 ~ S_SCAN3 : 0번째 행부터 3번째 행까지를 10msec 주기로 순회하면서 버튼이 눌렀는지
                                              여부 확인하는 상태 
    - S_KEY_PROCESS : 버튼이 눌렀을 경우, 어떤 버튼이 눌렀는지를 확인하고, 이를 출력으로 내보내는 상태

  • 소스 코드에 대한 자세한 설명은 아래 게시글을 참고하길 바란다.
    https://jbhdeve.tistory.com/275
 

Verilog RTL 설계(7월 22일 - 2, FSM 형식으로 Keypad 재구현)

1. FSM 기법이란?Finite State Machine시스템의 동작을 상태, 이벤트, 전이로 모델링하는 수학적 모델을 의미.시스템은 한 번에 단 하나의 상태만 가질 수 있다.  1.1. FSM의 장점복잡한 시스템 동작에 대

jbhdeve.tistory.com

 

 

 

 

< Source, Clock Divider 1000 >

// 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] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else if(clk_source_nedge) begin
            if(counter >= 999) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_1000 = (counter < 500)? 0 : 1;
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_1000), .n_edge(clk_div_1000_nedge), .p_edge(clk_div_1000_pedge));
    
endmodule

 

 

< Source, Clock Divider 100 >

// 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] 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 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

 

 

< Source, Edge detector >

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

 

 

< Source, Decoder of 7-Segment >

// Decoder of 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; //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

 

 

< Source, Ring Counter of com >

// Ring Counter
module ring_counter (
    input clk, reset_p,
    output reg [3:0] com );
    
    // Get 1msec One Cycle Pulse
    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

 

 

< Source, FND Control >

// Control FND
module fnd_cntr (
    input clk, reset_p,
    input [15:0] hex_value,
    output [3:0] com,
    output [7:0] seg_7 );
    
    // Ring Counter of com
    ring_counter ring_counter_0 (.clk(clk), .reset_p(reset_p), .com(com));
    
    // Select value & Print value on FND,
    reg [3:0] value;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) value = 4'b0;
        else 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
    end
    
    decoder_seg7 decoder_7_segment (.hex_value(value), .seg_7(seg_7));

endmodule

 

 

< Source, KeyPad Control >

// Control KeyPad
module Keypad_cntr(
    input clk, reset_p,
    input [3:0] col,
    output reg [3:0] row,
    output reg [3:0] key_value,
    output [4:0] state_led );
    
    // State LED
    assign state_led = state;
    
    // Declare State.
    parameter S_SCAN0            = 5'b00001;
    parameter S_SCAN1            = 5'b00010;
    parameter S_SCAN2            = 5'b00100;
    parameter S_SCAN3            = 5'b01000;
    parameter S_KEY_PROCESS      = 5'b10000;
    
    // Get 8msec One Cycle Pulse
    wire clk_10msec;   
    reg [19:0] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else counter = counter + 1;
    end
    
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(counter[19]), .n_edge(clk_10msec));
    
    
    // Declare Variable of state.
    reg [4:0] state, next_state;
    
    
    // 언제 다음 상태로 넘어가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin state = S_SCAN0; end
        else if(clk_10msec) state = next_state;
    end 
    
    
    // 다음 조건으로 넘어가기 위한 조건 설정
    always @(*) begin
            case(state) 
                S_SCAN0 : begin
                    if(col == 0) next_state = S_SCAN1;
                    else next_state = S_KEY_PROCESS; 
                end
                
                S_SCAN1 : begin
                    if(col == 0) next_state = S_SCAN2;
                    else next_state = S_KEY_PROCESS;
                end
                
                S_SCAN2 : begin
                    if(col == 0) next_state = S_SCAN3;
                    else next_state = S_KEY_PROCESS;
                end
                
                S_SCAN3 : begin
                    if(col == 0) next_state = S_SCAN0;
                    else next_state = S_KEY_PROCESS; 
                end
                
                S_KEY_PROCESS : begin
                    if(col == 0) next_state = S_SCAN0; 
                    else next_state = S_KEY_PROCESS;
                end
                
                default : next_state = S_SCAN0;
            endcase
    end
    
   
   // 각 state 마다 동작 설정.
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            key_value = 0;
            row = 0;
        end
        else begin
            case(state)
                S_SCAN0 : begin row = S_SCAN0; end
                S_SCAN1 : begin row = S_SCAN1; end
                S_SCAN2 : begin row = S_SCAN2; end
                S_SCAN3 : begin row = S_SCAN3; end
                S_KEY_PROCESS : begin
                    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

 

 

< Source, Top module of 4X4 Matrix KeyPad >

// Top Module of key pad
module top_module_of_keypad (
    input clk, reset_p,
    input [3:0] col,
    output [3:0] com, 
    output [7:0] seg_7,
    output [3:0] row,
    output [4:0] state_led);
    
    wire [3:0] key_value;
    Keypad_cntr control_keypad (.clk(clk), .reset_p(reset_p), .col(col), .row(row), .key_value(key_value), .state_led(state_led)); 
    
    fnd_cntr control_fnd (.clk(clk), .reset_p(reset_p), .hex_value({12'b0, key_value}), .com(com), .seg_7(seg_7));
endmodule

 

 

 

 

 

2. 구동 영상 

 

 

 

 

 

 

3. 전체 소스 코드

// Top Module of key pad
module top_module_of_keypad (
    input clk, reset_p,
    input [3:0] col,
    output [3:0] com, 
    output [7:0] seg_7,
    output [3:0] row,
    output [4:0] state_led);
    
    wire [3:0] key_value;
    Keypad_cntr control_keypad (.clk(clk), .reset_p(reset_p), .col(col), .row(row), .key_value(key_value), .state_led(state_led)); 
    
    fnd_cntr control_fnd (.clk(clk), .reset_p(reset_p), .hex_value({12'b0, key_value}), .com(com), .seg_7(seg_7));
endmodule

// Control KeyPad
module Keypad_cntr(
    input clk, reset_p,
    input [3:0] col,
    output reg [3:0] row,
    output reg [3:0] key_value,
    output [4:0] state_led );
    
    // State LED
    assign state_led = state;
    
    // Declare State.
    parameter S_SCAN0            = 5'b00001;
    parameter S_SCAN1            = 5'b00010;
    parameter S_SCAN2            = 5'b00100;
    parameter S_SCAN3            = 5'b01000;
    parameter S_KEY_PROCESS      = 5'b10000;
    
    // Get 8msec One Cycle Pulse
    wire clk_10msec;   
    reg [19:0] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else counter = counter + 1;
    end
    
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(counter[19]), .n_edge(clk_10msec));
    
    // Declare Variable of state.
    reg [4:0] state, next_state;
    
    // 언제 다음 상태로 넘어가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin state = S_SCAN0; end
        else if(clk_10msec) state = next_state;
    end 
    
    // 다음 조건으로 넘어가기 위한 조건 설정
    always @(*) begin
            case(state) 
                S_SCAN0 : begin
                    if(col == 0) next_state = S_SCAN1;
                    else next_state = S_KEY_PROCESS; 
                end
                
                S_SCAN1 : begin
                    if(col == 0) next_state = S_SCAN2;
                    else next_state = S_KEY_PROCESS;
                end
                
                S_SCAN2 : begin
                    if(col == 0) next_state = S_SCAN3;
                    else next_state = S_KEY_PROCESS;
                end
                
                S_SCAN3 : begin
                    if(col == 0) next_state = S_SCAN0;
                    else next_state = S_KEY_PROCESS; 
                end
                
                S_KEY_PROCESS : begin
                    if(col == 0) next_state = S_SCAN0; 
                    else next_state = S_KEY_PROCESS;
                end
                
                default : next_state = S_SCAN0;
            endcase
    end
    
    // 각 state 마다 동작 설정.
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            key_value = 0;
            row = 0;
        end
        else begin
            case(state)
                S_SCAN0 : begin row = S_SCAN0; end
                S_SCAN1 : begin row = S_SCAN1; end
                S_SCAN2 : begin row = S_SCAN2; end
                S_SCAN3 : begin row = S_SCAN3; end
                S_KEY_PROCESS : begin
                    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

// Control FND
module fnd_cntr (
    input clk, reset_p,
    input [15:0] hex_value,
    output [3:0] com,
    output [7:0] seg_7 );
    
    // Ring Counter of com
    ring_counter ring_counter_0 (.clk(clk), .reset_p(reset_p), .com(com));
    
    // Select value & Print value on FND,
    reg [3:0] value;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) value = 4'b0;
        else 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
    end
    
    decoder_seg7 decoder_7_segment (.hex_value(value), .seg_7(seg_7));

endmodule

// Ring Counter
module ring_counter (
    input clk, reset_p,
    output reg [3:0] com );
    
    // Get 1msec One Cycle Pulse
    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 @(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 (
    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

// 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] 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 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

// 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] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else if(clk_source_nedge) begin
            if(counter >= 999) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_1000 = (counter < 500)? 0 : 1;
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_1000), .n_edge(clk_div_1000_nedge), .p_edge(clk_div_1000_pedge));
    
endmodule

'RTL Design > Verilog 연습' 카테고리의 다른 글

DHT11 - 1  (0) 2024.08.11
10kHz인 PWM 설계 (Duty ratio stage 100단계) - (1)  (0) 2024.08.11
4X4 Matrix KeyPad - 1  (0) 2024.08.09
Stop Watch - 1  (0) 2024.08.07
Advanced Clock  (0) 2024.08.06