Verilog심화(1) - Counter & FSM

최보열·2022년 9월 25일
1

verilog심화

목록 보기
1/2
post-thumbnail

이번 글에서 다뤄볼 내용은 심화라기에는 좀 기본적인 FSM(Finite State Machine)을 다루고자 합니다. FSM이 어떤 것인지 대부분 알고 계실거라 생각이듭니다. FSM을 설명하고자 하는 것이 아니라 Counter라는 기본적인 sequential logic을 FSM으로 Control해보면서 FSM의 설계방식에 난해하였던 점을 몇 가지 같이 얘기해보려고 합니다.

다음은 위키백과에서 FSM에서 중요한 몇 가지 개념을 review차원으로 가져왔습니다.

  • FSM은 유한한(Finite) 개수의 상태(State)를 가질 수 있는 automata, 즉 abstract machine라고 할 수 있다.
  • 이러한 machine은 한 번에 오로지 하나의 state만을 가지게 되며, 현재상태(Current State)란 임의의 주어진 시간의 상태를 칭한다.
  • 이러한 기계는 어떠한 사건(Event)에 의해 한 상태에서 다른 상태로 변화할 수 있으며, 이를 전이(Transition)이라 한다.

먼저 가장 의문이 들었던 점은 'FSM이 무엇을 지칭하는 것인지?' 입니다. 논리회로 시간에 필수적으로 들어가는 내용으로 분명 강의도 들었지만 FSM의 정의를 물어보면 쉽게 답하지 못합니다. 저도 정확한 정의는 아니지만 가장 와닿았던 문장을 가져와봤습니다.

  • Finite-State-Machine(FSM)는 computer program과 electrical logic circuit을 설계하는 데에 쓰이는 mathematic model이다. 간단히 ‘state machine’라고 부르기도 한다.

Computer Architecture와 Electron분야의 발전으로 저희는 점점 더 어려운 인간 사회의 문제를 해결하기 위해서 더 어렵고 복잡한 logic들을 설계합니다. 당연히 해당 logic을 control해주는 control unit들이 필요하게 됩니다. 이때 어떤 process flow를 state라는 상태로 분할하여 해당 state에 필요한 동작과 신호들을 기술하여 control unit의 설계를 좀 더 수월하기 위한 수학적 방법론이라고 생각합니다.

다음은 FSM의 Verilog를 이용한 구성을 살펴보시겠습니다.

module 모듈이름 #(
	parameter 선언
)(
	input, output 선언
);	
	localparam 선언
    reg, wire 등 선언
    sequential 구문 선언
    combination구문 선언
    output assign구문 선언

endmodule

코딩 스타일에 따라 달리지겠지만 기본적으로 다음과 기술 문구들이 존재합니다. localparam을 이용하여서 state의 가시성을 높이고, sequential 구문으로 current state를 저장하는 flipflop을, combination구문에서 case문을 이용하여 state transition을 정의해주며, moore와 mealy에 따라서 state의 output을 결정해주는 구문이 존재합니다. 물론 assign구문과 같은 경우에는 위의 combination구문에 포함시켜도 되고, 이런저런 변형이 존재합니다. 기술 방식은 자유롭지만 불필요하거나 잘못된 구문을 기술하는 것에 주의를 가지셔야 되겠습니다.

서두가 너무 길었던거 같은데 실습으로 넘어가겠습니다. 이번 글에서 가져온 주제는 Counter라는 간단한 module을 FSM을 이용하여 사용자가 원하는 동작을 하도록 설계하는 것입니다.

해당 주제는 인프런의 '설계독학맛비'님의 Verilog HDL Season1 강의를 참고하였고, 코딩 방식은 변형을 하였습니다.

인프런 강좌 사이트 : https://www.inflearn.com/course/%EC%8B%A4%EC%A0%84-%EB%B2%A0%EB%A6%B4%EB%A1%9C%EA%B7%B8
Matbi 유튜브 : https://www.youtube.com/channel/UCnXNk4fSgnkxQGr36cze_6w

  • start와 cnt_val를 동시에 입력된다.
  • cycle마다 1씩 증가
  • count값과 cnt_val값이 동일해지는 cycle에 done이 1로 증가.

Waveform은 다음과 같습니다. start_i와 cnt_val_i값이 들어갑니다. cnt값이 1개씩 증가하면서 start_i를 주었을 때 같이 준 cnt_val값과 동일해지면 done_o을 1로 띄웁니다. 이런 동작을 위해서 저희는 매우 단순한 Counter를 먼저 설계하도록 하겠습니다.

Counter의 Spec입니다. 단순히 enable신호가 1이 되면 counter가 enable되면서 clock이 edge될때마다 1씩 cnt값을 증가시킵니다. 이를 코드로 구현하면 다음과 같습니다.

DWIDTH(Data Width)는 100까지 count할 예정임으로 7bit로 잡아줬습니다. 아래는 cnt라는 내부의 flipflop을 선언하고, enable이 들어올 때는 현재 cnt값에서 1이 더해진 값을 update해주고, 그 이외의 상황에는 always구문의 동작대로 "현재상태를 유지"하도록 합성될 것임으로 원하는 counter의 동작과 일치합니다.

아마 합성되면 위의 그림과 같이 구성되지 않을까 싶습니다.
다음은 FSM을 살펴보도록 하겠습니다. 먼저 FSM이 해주어야 되는 일을 state별로 분류해 보았습니다.

IDLE

  • cnt_val_i를 start_i 신호가 들어올 때 capture해주어야 합니다.
  • start_i신호가 들어오면 RUN state로 전이합니다.

RUN

  • counter에 enable값을 공급해주어야 합니다.
  • Counter로부터 현재 Counter가 세고 있는 값인 cnt값을 cnt_i로 받아서 cnt_val와 비교하고 cnt == cnt_val-1일 경우에 DONE으로 전이합니다.

DONE

  • DONE신호를 외부로 출력해줘야 합니다.
  • 아무 조건없이 1clock이 지나면 IDLE로 전이합니다.

cnt_val_i는 외부에서 들어오는 신호로 예를들어 100까지 count해야될 경우 100이 start_i와 함께 들어옵니다. 그런데 문제점은 해당 포트인 cnt_val_i에 counter의 동작이 끝날 때까지 100을 유지하고 있는지는 미지수입니다. 따라서 아래의 그림과 같이 cnt_val_i가 들어오면 이것을 하나의 flipflop에 저장해 둘 필요가 있습니다.

일단 spec은 여기까지이고, Controller코드를 작성하면서 내부 동작을 하나씩 설명드리도록 하겠습니다. 먼저 I/O port부분입니다. parameter로 counter와 controller에서 사용하기 위해서 동일하게 DWIDTH가 7인 것을 볼 수 있습니다.

다음 아래는 localparam으로 state를 가시성있게 기술하는 부분과 reg type variable을 선언해주는 부분입니다. 이번 글에서 작성할 verilog 코드는 제가 현재 지도받고 있는 교수님의 코딩 스타일을 가져온 것인데 보시면 current를 위한 reg와 next를 위한 reg가 먼저 보입니다.

current를 위한 state, cnt_val는 실제로 flipflop으로 합성될 reg variable를 의미하고, state_n과 cnt_val_n은 always를 이용한 combination구문에서 next state의 값들을 할당하기 위해서 선언한 reg type variable인데 실제로는 wire로 합성될 것입니다. 또한 아래의 run, done도 같은 맥락으로 wire로 선언될 예정입니다.

다음은 state를 update해주는 sequential을 기술한 부분인데 reset이면 초기화 시키고, reset이 아닌 경우에는 next state의 value로 update시켜줍니다.

다음은 이제 next state를 기술하는 combination구문입니다. 먼저 always아래에 바로 값들을 blocking으로 기술되어 있습니다. 이는 latch를 방지하기 위한 것으로 아래의 case구문에서 각 state별로 기술되어 있는 blocking 구문에 해당하지 않는다면 해당 구문이 실행됩니다. 즉 모든 경우를 기술하는 한 가지 방법입니다. default나 else를 이용해서 모든 경우를 기술하는 방식이 초기에는 많이 사용되는데 그러다보면 빠뜨리는 경우도 많이 발생하고 따라서 위와 같이 기술하는 방식이 많이들 사용됩니다.

state별로 확인해보면 IDLE에서 start_i가 들어오면 cnt_val_n = cnt_val_i를 이용해서 capture동작을 기술합니다. RUN state에서는 run값을 1로 출력하면서 counter의 enabl신호를 공급해주고, cnt_val == cnt_i를 이용해서 얼마나 count하고 있는지 확인하면서 done state로 전이할지 판단합니다. DONE같은 경우에는 done값을 1로 외부에 출력하고, state를 다음 clock에서 바로 IDLE로 전이합니다.

마지막으로 reg로 선언되어 있던 run, done을 외부 run_o과 done_o port로 연결해주기 위해서 assign구문을 이용해줍니다.

그럼 이제 counter와 counter_controller를 연결해주는 top module을 기술하도록 하겠습니다. top module은 외부와 interface를 구성하고 내부 controller(FSM)과 counter의 연결을 기술해주면 됩니다.

코드를 작성하면 다음과 같습니다. cnt값을 Controller로 전달하기 위해서 wire를 선언해주었고, 또한 run state일때 Controller에서 run을 1로 출력하여 counter의 enable신호로 사용하였습니다.(TB는 github참고)

Waveform은 testbench전략에 따라서 다음과 같이 reset으로 전체 모듈을 초기화 한 이후에 cnt값인 100과 start_i값을 동시에 주고 1thick이후에는 값을 0으로 줍니다. 이후에 계속 RUN을 하게되면 cnt값이 100이 될때 done이 뜨게 됩니다.

CODE.

REF.

profile
Computer Architecture 엔지니어 지망생 from SKKU

0개의 댓글