이번에는 베릴로그를 사용하여 FIFO 구조를 설계해보겠습니다. 설계는 CU(Control Unit)와 DP(Data Path)를 분리하는 방식으로 구성하였습니다.

베릴로그로 FIFO 구조를 설계하기 전 FIFO가 무엇인지 먼저 알아보겠습니다.

image.png

위 사진은 Queue(큐) 자료구조의 동작 과정을 표현한 것입니다.

Queue는 FIFO(First In First Out) 원칙에 따라 작동하며, 데이터는 한 쪽에서 들어오고(Enqueue) 반대쪽에서 나갑니다(Dequeue). 사진을 보면 Queue에 1~6의 데이터가 순서대로 추가되게 됩니다. 그리고 먼저 들어온 순서대로 데이터가 나가게 됩니다. 이렇게 데이터가 들어온 순서대로 나가는 구조가 FIFO 입니다.

아래 블록 다이어그램은 FIFO 회로 블록을 개념적으로 정리한 그림입니다.

image.png

FIFO의 핵심 기능은 push(쓰기), pop(읽기) 입니다. 그리고 full/empty로 큐가 가득 찼는지 비었는지 여부를 체크합니다. 아래 블록 다이어그램은 FIFO의 구조를 DP(Data Path)와 CU(Control Unit)으로 구분하여 시각화한 블록입니다.

**CU(Control Unit)**부터 보면 데이터를 입력하는 push 신호와 데이터를 출력하라는 pop 신호가 입력으로 들어가고 있습니다. 출력은 데이터 주소 포인터를 가리키는 w_ptr과 r_ptr 그리고 현재 FIFO의 상태를 나타내는 full과 empty가 있습니다.

**DP(Data Path)**는 wdata를 입력할 데이터(push data)로 받습니다. 그리고 rdata를 출력 데이터(pop data)로 내보내게 됩니다. wr_en은 쓰기 활성화 여부를 정하게 됩니다. 이때 wr_en은 CU에서 push & ~full 조건으로 생성됩니다.

image.png

push 신호가 입력 되었을 때 CU에서 full 상태가 아닌 경우 w_ptr이 증가합니다. 동시에 wr_en을 통해 wdata를 DP에 쓰게 됩니다. pop 신호가 입력되면 CU에서 empty 상태가 아닌 경우 r_ptr이 증가합니다. 이때 해당 위치의 데이터를 rdata로 읽어 출력하게 됩니다.

ℹ️ Verilog Code(fifo_cu)

fifo_cu 모듈은 클럭(clk)과 비동기 리셋(rst)을 입력받아, 데이터 쓰기 요청(push)과 읽기 요청(pop)에 따라 쓰기 포인터(w_ptr)와 읽기 포인터(r_ptr)를 관리하며, 현재 FIFO 상태를 나타내는 fullempty 출력을 제공합니다.

이 포인터 값은 외부 메모리 모듈에서 주소로 사용되며, full은 FIFO가 가득 찼을 때, empty는 비어 있을 때 활성화됩니다.