[SoC] Asynchronous FIFO 개념 및 설계(Verilog코드)

새옹지마·2025년 1월 9일

SoC

목록 보기
4/5
post-thumbnail

저번 글에서는 서로 다른 clock 도메인간의 통신에서 Gray code가 가지는 신뢰성에 대해서 알아보았습니다. Gray Code는 Asynchronous FIFO의 Pointer로 사용하게 되는데, gray counter의 형태로 사용됩니다. Asynchronous FIFO에 대해 공부하기 전에 gray counter부터 공부하겠습니다.


Gray counter

말 그대로 counter인데, binary counter가 아닌 gray counter입니다. 어떤 방법으로 gray counter를 verilog로 구현할 수 있을까요? 또 FIFO의 어디에 사용될까요?

Gray code를 보면 0~7, 8~15가 MSB를 제외한 하위 3bit는 대칭 구조임을 그림을 통해 알 수 있습니다.

FIFO에서는 pointer의 하위비트로 buffer address를 표현합니다. 예를 들어, Pointer가 4비트면 하위 3비트는 buffer address로 사용됩니다. 하지만 gray code의 하위 3비트를 사용하게 되면, 7에서 8이 될 때, 하위 3비트의 변화가 없습니다. 또한 Binary에서는 0~7의 하위 3비트가 8~15의 하위 3비트와 순서와 값이 일치하는데, gray code에서는 그러지 못합니다. 제대로된 buffer address의 역할을 하지 못합니다.

따라서 4비트의 pointer를 address로 변환할 때, 상위 2비트를 xor([3]^[2])하는 방법을 사용합니다.
Gray          Converted Address              Gray           Converted Address
0(0000) --> 0(000)                        <-> 15(1000) --> 7(100)
1(0001) --> 1(001)                        <-> 14(1001) --> 6(101)
2(0011) --> 2(011)                        <-> 13(1011) --> 5(111)
3(0010) --> 3(010)                        <-> 12(1010) --> 4(110)
4(0110) --> 4(110)                        <-> 11(1110) --> 3(010)
5(0111) --> 5(111)                        <-> 10(1111) --> 2(011)
6(0101) --> 6(101)                        <-> 9  (1101) --> 1(001)
7(0100) --> 7(100)                        <-> 8  (1100) --> 0(000)

그러면 buffer address가 0~7까지 갔다가 다시 0~7을 반복하게 됩니다.
이를 공식화하면 아래와 같습니다.

요약하자면 Clock Domain을 넘어가는 Code는 N비트 Gray Code이지만, 하위 (N-1)비트를 그대로 address로 사용하지 않고, 위와 같은 공식을 통해서 (N-1)bit Gray code Counter로 변환하여 buffer address로서 사용합니다.


Asynchronous FIFO 개념

CDC를 수행할 때, Multi-bit의 전송이 필요할 때 많이 사용되는 Asynchronous FIFO에 대해 알아보겠습니다.
Asynchronous FIFO에서 가장 중요한 점은 Asynchronous FIFO의 데이터가 아닌 Write Domain과 Read Domain의 Pointer에 CDC(Clock Domain Crossing) 기법을 적용한다는 것입니다.  포인터가 핵심!!
Asynchronous FIFO에 저장된 데이터가 stable해야하는거 아니냐구요? 데이터는 Pointer들의 CDC가 이뤄지고, Full/Empty를 판별하는 것으로 Stable한 상태임을 보장받습니다. Full/Empty가 확실하게 stable하면 데이터도 stable하다는 뜻입니다.

Asynchronous FIFO에서 write 할때는 Read domain으로부터 read pointer를 받아 full 신호를 판단하고 read를 할때는 wirte domain으로부터 write pointer를 받아 empty를 판단합니다.


Asynchronous FIFO 구현 (SystemVerilog)

일반 Binary Counter를 Pointer로 사용하는 FIFO의 Empty와 Full 조건
1. Empty Condition

 if(Write Pointer == Read Pointer) Empty = 1;
 else Empty = 0;

2. Full Condition

if((Write Pointer[MSB] != Read Pointer[MSB]) 
	&& (Write Pointer[MSB-1:0] == Read Pointer[MSB-1:0])) Full = 1;
else Full = 0;

같은 방식을 Gray Counter를 사용하는 Pointer에 적용하면 아래와 같다.
1. Empty Condition

if(Write Gray Pointer == Read Gray Pointer) Empty = 1;
else Empty = 0;

2. Full Condition

if	(Write Gray Pointer[MSB] != Read Gray Pointer[MSB]) 
	&& (Write Gray Pointer[MSB-1] != Read Gray Pointer[MSB-1]) 
	&& (Write Gray Pointer[MSB-2:0] == Read Gray Pointer[MSB-2:0])) Full = 1;
else Full = 0;

Pointer로부터 buffer address를 추출하여 FULL/EMPTY Condition을 확인합니다.
이것만 알아두면 쉽게 Asynchronous FIFO를 코드로 구현할 수 있습니다.

코드는 인프런 강의의 삼코치님 코드를 참고했습니다.


Module(SystemVerilog)

module async_fifo #(      
   parameter   DEPTH=8,
   parameter   WIDTH=8,
     parameter   DEPTH_LOG=$clog2(DEPTH)
)(
   input              rstn,
   // WCLK DOMAIN
   input              wclk,   
   input              push,   
   input [WIDTH-1:0]    din,
   output             full,
   // RCLK DOMAIN
   input              rclk,
   input              pop,
   output [WIDTH-1:0]    dout,   
   output             empty
);

   reg  [WIDTH-1:0]    mem [DEPTH-1:0];
   
   // WCLK DOMAIN
   reg  [DEPTH_LOG:0]    wptr_bin;       
   wire [DEPTH_LOG:0]    wptr_gray;       
   reg  [DEPTH_LOG:0]    rptr_gray_meta, rptr_gray_wclk; 
   
   // RCLK DOMAIN
   reg  [DEPTH_LOG:0]    rptr_bin;        
   wire [DEPTH_LOG:0]    rptr_gray;   
   reg  [DEPTH_LOG:0]    wptr_gray_meta, wptr_gray_rclk; 
   
   // WCLK DOMAIN
   always @(posedge wclk or negedge rstn)
   if       (!rstn)   for (int i=0;i<DEPTH;i++)     mem[i] <= 0;       //mem 초기화
   else if (push & !full)   mem[wptr_bin[DEPTH_LOG-1:0]] <= din;       //push는 write domain
   
   
   always @(posedge wclk or negedge rstn)          //push신호가 들어오면 full이 아닌 경우에 ptr+1
   if       (!rstn)         wptr_bin   <= 0;
   else if (push & !full)   wptr_bin    <= wptr_bin + 1;         
   
   assign wptr_gray       = bin2gray(wptr_bin);   //gray신호로 변환
   
   always @(posedge wclk or negedge rstn)   //synchronizer를 이용하여 RCLK 도메인 신호를
   if       (!rstn)   begin                 //WCLK 도메인으로 전달
         rptr_gray_meta <= 0;
         rptr_gray_wclk <= 0;
    end else       begin
         rptr_gray_meta <= rptr_gray;
         rptr_gray_wclk <= rptr_gray_meta;   //synchronizer
    end         
   //GRAY STYLE full condition
   assign full   = (wptr_gray[DEPTH_LOG-:2]  == ~rptr_gray_wclk[DEPTH_LOG-:2] ) &&  
                (wptr_gray[DEPTH_LOG-2:0] == rptr_gray_wclk[DEPTH_LOG-2:0]);         

   // RCLK DOMAIN
   always @(posedge rclk or negedge rstn)           //pop
   if       (!rstn)         rptr_bin <= 0;
   else if (pop & ~empty)   rptr_bin <= rptr_bin + 1;
   
   assign rptr_gray       = bin2gray(rptr_bin);      //gray신호로 변환
   
   always @(posedge rclk or negedge rstn)           //synchronizer를 이용하여 WCLK 도메인 신호를
   if       (!rstn)   begin                         //RCLK 도메인으로 전달
      wptr_gray_meta  <= 0;
      wptr_gray_rclk  <= 0;
   end else      begin
      wptr_gray_meta <= wptr_gray;
      wptr_gray_rclk <= wptr_gray_meta;
   end
   //GRAY STYLE empty condition
   assign empty   = (rptr_gray == wptr_gray_rclk);
   
   //This will be read when data is stable enough
   assign dout = mem[rptr_bin[DEPTH_LOG-1:0]];  //pop data값
   
   function [DEPTH_LOG:0] bin2gray ;            //binary code를 gray code로 변환하는 function
      input  [DEPTH_LOG:0] bin;
      begin
         bin2gray = (bin>>1) ^ bin;
      end
   endfunction
      
endmodule

Waveform(Vivado)

  • full인 상태에서 push와 pop이 동시에 진행되면 full은 유지됩니다.
  • pointer신호들의 clk동기화 여부를 확인하시면 되겠습니다.

개념이 생각보다 어려웠습니다.
자세한 내용은 아래 Reference 참고 부탁드립니다.

댓글과 하트 감사합니다😍


Reference

  1. https://secondspot.tistory.com/26 [소소하게 일상을 기록하는 페이지:티스토리]
  2. https://blog.naver.com/ms2000311/223562476525
  3. https://aignacio.com/posts/hdls/cdc_lib/
profile
반도체, HW ,SW 탐구생활

0개의 댓글