Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- pwm
- LED
- ring counter
- half adder
- stop watch
- dataflow modeling
- atmega 128a
- prescaling
- D Flip Flop
- structural modeling
- BASYS3
- vivado
- behavioral modeling
- DHT11
- soc 설계
- uart 통신
- Linked List
- java
- FND
- Recursion
- verilog
- hc-sr04
- Pspice
- KEYPAD
- gpio
- test bench
- ATMEGA128A
- i2c 통신
- Edge Detector
- Algorithm
Archives
- Today
- Total
거북이처럼 천천히
Verilog RTL 설계(7월 22일 - 2, FSM 형식으로 Keypad 재구현) 본문
1. FSM 기법이란?
- Finite State Machine
- 시스템의 동작을 상태, 이벤트, 전이로 모델링하는 수학적 모델을 의미.
- 시스템은 한 번에 단 하나의 상태만 가질 수 있다.
1.1. FSM의 장점
- 복잡한 시스템 동작에 대해서 유한 상태로 나눈 뒤, 각각의 상태에 대한 동작과 다른 상태로 전이를 위한 조건을 정의함으로서 복잡한 시스템을 단순화할 수 있다.
- 시스템의 동작을 여러 상태로 나누었기 때문에 디버깅 과정에서 문제가 발생했을 때, 어느 부분에서 문제가 발생했는지를 쉽게 파악하여 빠르게 고칠 수 있다.
1.2. FSM와 상태도(State diagram)과의 관계
- FSM은 시스템의 동작을 여러 상태로 나누어 현재 상태의 작업을 수행한 뒤, 발생한 이벤트에 따라 다른 이벤트로 전이된다.
- FSM의 시스템의 여러 상태를 쉽게 파악하기 위해서 시각적인 자료가 필요하며, 이러한 시각적인 자료의 역활을 수행 할 수 있는 것이 State diagram이다.
- State diagram을 통해 각각의 상태에서의 동작과 다른 상태로 전이되기 위한 조건을 한 눈에 확인할 수 있어. FSM으로 설계할 때, 밀접하게 사용된다.
2. 4X4 Matrix KeyPad을 FSM (Finite State Machine)으로 재구현 해보자.
- FSM (Finite State Machine)을 공부하기 위해 4X4 Matrix KeyPad를 다시 구현해보도록 하겠다.
2.1. FSM 설계 전, 상태도(State diagram)에 대해서 공부해보자.
- 이전에 설계했던 4X4 Matrix keypad에 대한 상태도를 그리면 다음과 같다.
4X4 Matrix Keypad에 대한 설명을 다시하자면 다음과 같다.
- Matrix Keypad의 Row 단자에 Ring Counter를 이용하여 순차적으로 1 값의 신호를 준다.
(단, 여러개의 Row 단자 중 단 하나의 Row 단자에만 1값을 주고, 나머지 Row 단자에는 0 값을 준다.) - Ring Counter는 10ms Positive edge마다 순차적으로 각각의 Row단자에 1 값을 준다.
- Row 단자의 입력된 값이 1인 행에 속해 있는 단자들 중에서 현재 눌려진 버튼이 있는지 여부를 확인한다.
- 만약, 현재 눌러진 버튼이 있다면 Row 값(R1, R2, R3, R4)과 Col 값(C1, C2, C3, C4)을 교차하여 어떤 버튼이 눌렸는지를 확인하게 된다.
위 동작 과정을 기반으로 State diagram을 그리면 위와 같이 그릴 수 있으며, State diagram에 대해서 설명하자면 다음과 같다.
- 현재 Row 단자는 총 4줄이기 때문에 각각의 Row 단자에 1이 들어갔을 경우는 총 4가지로 나타나며, 이를 경우의 수로 구분하자면 총 4가지 케이스가 나올 수 있어 이를 scan1, scan2, scan3, scan4로 명명하였다.
- Ring Counter는 10ms Positive edge 을 주기로 순차적으로 Row 단자에 1값을 주기 때문에 상태의 변화 조건으로 10ms Positive edge라 작성하였다.
- 만약, 현재 1값이 들어가고 있는 Row에 속해있는 버튼이 눌려졌다면 이를 알려주기 위해 Key_valid 변수 값을 1로 설정하며, 이를 처리하기 위해 key_process 상태로 전이된다.
(즉, 다시 말해 scan1 ~ scan4 상태에서 속해 있는 버튼 중 버튼 눌림이 감지 된다면 key_valid 변수에 1값을 넣고, 이를 처리하기 위해 key_process 상태로 전이 된다.) - key_process 상태에 입력값을 출력한 뒤, 더 이상의 버튼 입력이 사라지면 다시 scan0로 이동하여 다시 처음부터 순차적으로 각 행에 1값을 주게 된다.
2.2. State diagram을 기반으로 코드를 작성하면 다음과 같이 작성할 수 있다.
< 시스템의 동작의 단계를 나누어 상태로 표현 >
// 상태 상수 정의
parameter SCAN0 = 5'b00001;
parameter SCAN1 = 5'b00010;
parameter SCAN2 = 5'b00100;
parameter SCAN3 = 5'b01000;
parameter KEY_PROCESS = 5'b10000;
- 상태도를 보면 알 수 있듯이 첫 번쩨 행부터 네 번째 행까지 1값의 신호를 넣는 경우와 1값이 들어온 행에 속해 있는 버튼이 눌려 졌을 때, 이에 대한 처리를 하는 경우로, 총 5단계로 나누어 상태로 표현했다.
< 현재 상태와 다음 상태의 변수 선언 >
// define State, next_State Value
reg [4:0] state, next_state;
- state는 "현재 있는 상태"를 의미하며, next_state는 "다음 전이될 상태"를 의미한다.
< 언제 다음 상태로 전이되는가? >
// state machine start.
// 어떤 경우에 상태가 변화하는가?
// clk_10ms_pedge가 활성화 될 때마다 전이 된다.
always @(posedge clk or posedge reset_p) begin
if(reset_p) state = SCAN0;
else if(clk_10ms_pedge) state = next_state;
end
- 시스템 시작과 함께 시행되는 초기화 작업에서 현재 상태를 SCAN0으로 초기화한다.
즉, 첫 번째 행부터 1 값의 신호를 주게 된다. - 주기가 10ms인 Pulse에서 Positive edge일 때, 현재 상태 변수인 state에 다음 상태 변수인 next_state를 대입함으로서 다음 상태로 전이되게 된다.
< 다음 상태로 전이되기 위한 조건은 무엇인가? >
// next_state를 정하자.
// row 값이 0이 아니면 현재 행에 속해있는 4개의 버튼 중 입력이 들어왔음을 의미
// 따라서 입력된 버튼의 값을 읽기 위해서 KEY_PROCESS 상태로 전이
// row 값이 0이면 눌러진 버튼이 없기 때문에 다음 행으로 이동.
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 상태에서 계속 버튼이 눌러진 상태라면
// 버튼을 뗄때까지 계속 처리하기 위해 KEY_PROCESS 상태를 유지
KEY_PROCESS : begin
if(col == 0) next_state = SCAN0;
else next_state = KEY_PROCESS;
end
default : next_state = SCAN0;
endcase
end
- 이전 블록에서 언제 다음 상태로 전이되는가에 대해서 정의를 했다면, 현재 블록에서는 어떤 이벤트가 발생했을 때, 어느 상태로 넘어갈지를 정해주게 된다. 즉, 다음 상태를 발생한 이벤트에 따라 선택하게 된다.
- "col == 0" 이라는 의미는 "현재 신호 값을 1 주고 있는 행에 속해 있는 버튼이 눌러지지 않았음"을 의미한다.
- 따라서, 눌러진 버튼이 없기 때문에 "다음 행에 1값을 주는 경우 및 상태"를 next_state로 설정하고, 다음 10ms positive edge를 기다린다.
- 반면에 "col == 1" 이라는 의미는 "현재 신호 값을 1 주고 있는 행에 속해 있는 버튼 중 하나의 버튼이 눌렀음"을 의미한다.
- 따라서, 어떤 버튼이 눌렀으며, 눌러진 버튼을 출력으로 내보내기 위한 작업을 수행하기 위해 KEY_PROCESS 으로 next_state를 설정하고, 다음 10ms positive edge를 기다린다.
- 만약, 현재 KEY_PROCESS이라면 com == 0일 경우에 다시 첫 번째 행(SCAN0)으로 이동해 확인하게 되며, com == 1일 경우 눌러진 버튼이 끝날 때까지 계속해서 값을 읽게 된다.
< 각 상태에서의 수행되는 작업들을 정의 >
// 각 상태에 따른 처리 과정
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
key_value = 0;
key_valid = 0;
row = 0;
end
else 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 = 4'h0;
8'b0001_0010 : key_value = 4'h1;
8'b0001_0100 : key_value = 4'h2;
8'b0001_1000 : key_value = 4'h3;
8'b0010_0001 : key_value = 4'h4;
8'b0010_0010 : key_value = 4'h5;
8'b0010_0100 : key_value = 4'h6;
8'b0010_1000 : key_value = 4'h7;
8'b0100_0001 : key_value = 4'h8;
8'b0100_0010 : key_value = 4'h9;
8'b0100_0100 : key_value = 4'ha;
8'b0100_1000 : key_value = 4'hb;
8'b1000_0001 : key_value = 4'hc;
8'b1000_0010 : key_value = 4'hd;
8'b1000_0100 : key_value = 4'he;
8'b1000_1000 : key_value = 4'hf;
default : key_value = key_value;
endcase
end
endcase
end
end
- 이전 단계에서 "언제 다음 상태로 전이되는가?"와 "어떤 이벤트가 발생했을 때, 어느 상태로 전이 될건가?"에 대해서 정의하였다.
- 이번 블록에서는 "각 상태에서 수행해야 할 작업들" 에 대해서 정의한다.
- 현재 상태가 SCAN0 ~ SCAN3 일 경우, 아직 버튼이 눌러지지 않은 상태이기 때문에 key_valid = 0로 정의하게 된다.
- 반면에 현재 상태가 KEY_PROCESS 이면 버튼이 눌러졌기 때문에 key_valid = 1로 정의하고, case문와 row 값과 col 값을 대조하여 현재 어떤 버튼이 눌러졌음을 확인하고, 눌러진 값을 key_value에 저장하고, 출력으로 내보내게 된다.
3. 구현 영상
4. 전체적인 소스 코드
module keypad_test_top (
input clk, reset_p,
input [3:0] col,
output [3:0] row,
output [3:0] com,
output [7:0] seg_7 );
wire [3:0] key_value;
wire key_valid;
Matrix_KeyPad_4X4_FSM keypad_fsm(.clk(clk), .reset_p(reset_p), .row(row), .col(col), .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 Matrix_KeyPad_4X4_FSM(
input clk, reset_p,
input [3:0] col,
output reg [3:0] row,
output reg [3:0] key_value,
output reg key_valid);
// 상태 상수 정의
parameter SCAN0 = 5'b00001;
parameter SCAN1 = 5'b00010;
parameter SCAN2 = 5'b00100;
parameter SCAN3 = 5'b01000;
parameter KEY_PROCESS = 5'b10000;
// Get 10ms One Cycle Pulse for ring Counter.
reg [19:0] count;
always @(posedge clk) count = count + 1;
wire clk_10ms_pedge;
edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(count[19]), .p_edge(clk_10ms_pedge));
// define State, next_State Value
reg [4:0] state, next_state;
// state machine start.
// 어떤 경우에 상태가 변화하는가?
// clk_10ms_pedge가 활성화 될 때마다 전이 된다.
always @(posedge clk or posedge reset_p) begin
if(reset_p) state = SCAN0;
else if(clk_10ms_pedge) state = next_state;
end
// next_state를 정하자.
// row 값이 0이 아니면 현재 행에 속해있는 4개의 버튼 중 입력이 들어왔음을 의미
// 따라서 입력된 버튼의 값을 읽기 위해서 KEY_PROCESS 상태로 전이
// row 값이 0이면 눌러진 버튼이 없기 때문에 다음 행으로 이동.
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 상태에서 계속 버튼이 눌러진 상태라면
// 버튼을 뗄때까지 계속 처리하기 위해 KEY_PROCESS 상태를 유지
KEY_PROCESS : begin
if(col == 0) next_state = SCAN0;
else next_state = KEY_PROCESS;
end
default : next_state = SCAN0;
endcase
end
// 각 상태에 따른 처리 과정
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
key_value = 0;
key_valid = 0;
row = 0;
end
else 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 = 4'h0;
8'b0001_0010 : key_value = 4'h1;
8'b0001_0100 : key_value = 4'h2;
8'b0001_1000 : key_value = 4'h3;
8'b0010_0001 : key_value = 4'h4;
8'b0010_0010 : key_value = 4'h5;
8'b0010_0100 : key_value = 4'h6;
8'b0010_1000 : key_value = 4'h7;
8'b0100_0001 : key_value = 4'h8;
8'b0100_0010 : key_value = 4'h9;
8'b0100_0100 : key_value = 4'ha;
8'b0100_1000 : key_value = 4'hb;
8'b1000_0001 : key_value = 4'hc;
8'b1000_0010 : key_value = 4'hd;
8'b1000_0100 : key_value = 4'he;
8'b1000_1000 : key_value = 4'hf;
default : key_value = key_value;
endcase
end
endcase
end
end
endmodule
// Control FND.
module fnd_cntr (
input clk, reset_p,
input[15:0] value,
output reg [3:0] com,
output [7:0] seg_7);
wire clk_1ms, clk_1ms_nedge;
reg [17:0] count;
always @(posedge clk or posedge reset_p) begin
if(reset_p) count = 0;
else begin
if(count >= 99999) count = 0;
else count = count + 1;
end
end
assign clk_1ms = (count < 50000) ? 0 : 1;
edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_1ms), .n_edge(clk_1ms_nedge));
always @(posedge clk or posedge reset_p) begin
if(reset_p) com = 4'b1110;
else if(clk_1ms_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
decoder_seg7 decoder_seg (.hex_value(hex_value), .seg_7(seg_7));
endmodule
// clk_div_n
module clk_div_n (
input clk, reset_p,
input clk_source,
input [31:0] prescale,
output clk_div_n,
output clk_div_nedge);
wire clk_source_nedge;
edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_sourcen), .n_edge(clk_source_nedge));
integer count;
always @(posedge clk or posedge reset_p) begin
if(reset_p) count = 0;
else if(clk_source_nedge) begin
if(count >= prescale - 1) count = 0;
else count = count + 1;
end
end
assign clk_div_n = (count < prescale/2)? 0 : 1;
edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_n), .n_edge(clk_div_nedge));
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
'RTL Design > Verilog RTL 설계' 카테고리의 다른 글
Verilog RTL 설계(7월 24일 - 2, HC-SR04 기초) (1) | 2024.07.25 |
---|---|
Verilog RTL 설계(7월 23일 - 1, DHT 기초) (2) | 2024.07.24 |
Verilog RTL 설계(7월 22일 - 1, 4X4 Matrix Keyboard - 1) (4) | 2024.07.23 |
Verilog RTL 설계(7월 18일 - 5, Stop Watch - 4) (0) | 2024.07.22 |
Verilog RTL 설계(7월 18일 - 4, Stop Watch - 3) (0) | 2024.07.22 |