Finite State Machine, 이하 FSM은 제어 회로에서 주로 사용된다. 레지스터 변수에 특정 상태(state) 값을 지정하고 그 값에 따라 제어신호를 변화시키는 방식이다. 아주 간단한 FSM을 구현해봤다.
module fsm_ex1(
output [1:0] state_out,
output reg done,
input clk, rst, start
);
reg [1:0] state;
reg [3:0] count;
assign state_out = state;
always @ (posedge clk) begin
if (rst) begin
state <= 3'b0;
done <= 1'b0;
count <= 4'b0;
end
else if (start) begin
count <= count + 4'b1;
if (count == 4'd0) begin
state <= 2'd0;
end
else if (count == 4'd4) begin
state <= 2'd1;
end
else if (count == 4'd8) begin
state <= 2'd2;
end
else if (count == 4'd12) begin
state <= 2'd3;
done <= 1'b1;
end
end
else begin
state <= 2'b0;
count <= 4'b0;
done <= 1'b0;
end
end
endmodule
카운터 값에 따라 상태 변수가 달라지는 코드다. 시간에 따라서 회로를 제어해야 한다면 카운터가 필수적이다. 왜냐하면 실제 합성을 하는 코드는 내부에 delay를 줄 수 없기 때문이다. initial 또한 불가능하다. 그래서 일정 clock cycle 마다 증감하는 카운터를 통해 제어를 하게 된다.
혼자서 모든 걸 다 만든다는 것은 힘들기 때문에, 혼자서 알아볼 수 있는 코드는 좋은 코드가 아니다. parameter와 같이 쓰이면 어느 상태인지 보기가 쉬워진다. 정확히는 어떤 state가 있는지, 어떻게 변화하는지를 알기 쉽게 해 준다.
module fsm_ex1(
output [1:0] state_out,
output reg done,
input clk, rst, start
);
parameter IDLE = 2'd0;
parameter STATE0 = 2'd1;
parameter STATE1 = 2'd2;
parameter STATE2 = 2'd3;
reg [1:0] state;
reg [3:0] count;
assign state_out = state;
always @ (posedge clk) begin
if (rst) begin
state <= 3'b0;
done <= 1'b0;
count <= 4'b0;
end
else if (start) begin
count <= count + 4'b1;
if (count == 4'd0) begin
state <= IDLE;
end
else if (count == 4'd4) begin
state <= STATE0;
end
else if (count == 4'd8) begin
state <= STATE1;
end
else if (count == 4'd12) begin
state <= STATE2;
done <= 1'b1;
end
end
else begin
state <= 2'b0;
count <= 4'b0;
done <= 1'b0;
end
end
endmodule
해당 코드는 그냥 state 변수를 output으로 내보냈지만, 제어 신호를 만들 수도 있을 것이다.
module fsm_ex1(
output [1:0] state_out,
output reg done, ctrl0, ctrl1, ctrl2,
input clk, rst, start
);
parameter IDLE = 2'd0;
parameter STATE0 = 2'd1;
parameter STATE1 = 2'd2;
parameter STATE2 = 2'd3;
reg [1:0] state;
reg [3:0] count;
assign state_out = state;
always @ (posedge clk) begin
if (rst) begin
state <= 3'b0;
done <= 1'b0;
count <= 4'b0;
ctrl0 <= 1'b0;
ctrl1 <= 1'b0;
ctrl2 <= 1'b0;
end
else if (start) begin
count <= count + 4'b1;
case (state)
IDLE: begin
ctrl0 <= 1'b0;
ctrl1 <= 1'b0;
ctrl2 <= 1'b0;
end
STATE0: begin
ctrl0 <= 1'b1;
ctrl1 <= 1'b0;
ctrl2 <= 1'b0;
end
STATE1: begin
ctrl0 <= 1'b0;
ctrl1 <= 1'b1;
ctrl2 <= 1'b0;
end
STATE2: begin
ctrl0 <= 1'b0;
ctrl1 <= 1'b0;
ctrl2 <= 1'b1;
end
default: begin
ctrl0 <= 1'b0;
ctrl1 <= 1'b0;
ctrl2 <= 1'b0;
end
endcase
if (count == 4'd0) begin
state <= IDLE;
end
else if (count == 4'd4) begin
state <= STATE0;
end
else if (count == 4'd8) begin
state <= STATE1;
end
else if (count == 4'd12) begin
state <= STATE2;
done <= 1'b1;
end
end
else begin
state <= 2'b0;
count <= 4'b0;
done <= 1'b0;
ctrl0 <= 1'b0;
ctrl1 <= 1'b0;
ctrl2 <= 1'b0;
end
end
endmodule
여기선 ctrl0~2라는 임의의 신호로 출력을 내보내지만, 알아볼 수만 있다면 어떤 이름을 써도 문제없다.
state 값에 따라 제어신호가 변하기 때문에 state 값 변화에 1 clock cycle만큼 뒤쳐지는데, case(state)를 case(count)로 바꾸면 해결되는 문제다. 이건 그냥 예시라서 딱히 수정을 가하진 않았지만, clock cycle에 민감한 제어 로직이 필요한 경우 참고 해두길 바란다.
구현할 수 있는 FSM은 매우 다양하고 복잡도도 각 로직마다 다르기 때문에, 예시는 예시로 알고 넘어가면 좋을 것 같다. 예를 들어 nested counter에 따라 변하기도 하고, 그 카운터의 트리거 역할을 하는 counter에 따라 변하게끔 모듈을 구현할 수도 있다.
경우의 수가 매우 커지기 때문에 복잡한 제어 로직에서는 제대로 제어신호를 발생시키는지 확인하는 과정은 필수적이라고 볼 수 있다.
만약 계획한 제어 신호 중 하나라도 어긋나면 민감한 회로의 경우 동작이 엉망으로 되는 것을 확인할 수 있을 것이다. 설계 시에는 주의하여 설계하길 바란다.
'Verilog HDL 설계' 카테고리의 다른 글
include의 필요성 및 사용법 (0) | 2022.04.12 |
---|---|
파라미터를 이용한 모듈 선언 (0) | 2022.04.07 |
테스트벤치 작성 예시 (0) | 2021.04.25 |
Countdown control & display (0) | 2021.04.23 |
BCD to 7 segment (0) | 2021.04.19 |