본문 바로가기
IT_Study/ASIC_Study

[CDC] 10. Asynchronous FIFO(4)

by 두번째얼룩 2022. 4. 25.

이전 포스트들을 통해서 Asynchronous FIFO를 설계하기 위한 기본적인 사항들을 알아보았다. Meta-stability 상태를 제거하기 위하여 2-Stage Synchronizer, Multi-bit CDC를 하기 위한 Gray Code, FIFO의 Empty, Full 상태를 알아보기 위한 Pointer 연산까지 살펴보았다. 이 사항들을 조합하여 Asynchronous FIFO를 설계해보도록 하자. 

Asynchronous FIFO를 이해함에 있어서 가장 중요한 점은 Asynchronous FIFO에 저장되는 데이터가 아니라 Write Domain과 Read Domain의 Pointer에 CDC(Clock Domain Crossing) 기법을 적용한다는 것이다. 

Asynchronous FIFO에 저장된 데이터는 Pointer들의 CDC가 이뤄지고, Full/Empty를 판별하는 것으로 Stable한 상태임을 보장받는다. 이 점을 기억하고 아래 설명을 보면 이해가 쉬울 것이다. 

첫 번째로 Write Domain의 Pointer를 준비한다. 이 Pointer는 FIFO에 Push를 수행할 때마다 +1씩 증가하는 값이다. +1을 증가시키는 과정은 아래와 같이 Binary Code를 이용하여 수행한다. 그리고 Write Pointer가 Read Pointer를 뛰어넘지 못하도록 full이 아닐 경우에만 Pointer를 증가시켜준다. 

ex) Write Pointer

if(push & !full) wptr <= wptr + 1

else                wptr <= wptr 

위에서 이야기 한 것처럼 Write Pointer를 Read Domain으로 넘기기 위하여 Gray Code로 변환한다. 

ex) Gray Code Conversion

assign wptr_gray = (wptr>>1) ^ wptr;

Gray Code로 변환된 Pointer를 Read domain으로 2-Stage Synchronizer를 이용하여 전달한다.

 

always @ (posedge rclk or negedge rstn) begin

    if(!rstn) begin

        wptr_gray_1st <= '0;

        wptr_gray_2nd <= '0;

    end

    else begin

        wptr_gray_1st <= wptr_gray;

        wptr_gray_2nd <= wptr_gray_1st;

    end

end

 

두번째로 Read Domain의 Pointer를 준비한다. 이 Pointer는 FIFO에 Pop을 수행할 때마다 +1씩 증가하는 값이다. +1을 증가시키는 과정은 아래와 같이 Binary Code를 이용하여 수행한다. 그리고 Read Pointer가 Write Pointer를 뛰어넘지 못하도록 full이 아닐 경우에만 Pointer를 증가시켜준다. 

 

if(pop & !empty) rptr <= rptr + 1

else                   rptr <= rptr 

 

Gray Code로 변환된 Pointer를 Write domain으로 2-Stage Synchronizer를 이용하여 전달한다.

assign rptr_gray = (rptr>>1) ^ rptr;

 

always @ (posedge wclk or negedge rstn) begin

    if(!rstn) begin

        rptr_gray_1st <= '0;

        rptr_gray_2nd <= '0;

    end

    else begin

        rptr_gray_1st <= rptr_gray;

        rptr_gray_2nd <= rptr_gray_1st;

    end

end

 

세번째로 Write Domain에 데이터를 저장할 공간을 마련한다. 아래 예제에서는 Register를 사용하였다.

(SRAM 등 Memory를 사용하는 경우도 종종 있다.)

 

always @ (posedge wclk or negedge rstn) begin

    if(!rstn) begin

        mem <= '0;

    end

    else begin

        if(push) mem[wptr] <= input_data;

        else      mem[wptr] <= mem[wptr];

    end

end

 

네 번째로는 Read Domain에서 데이터를 꺼내가는 로직을 구비한다. Read data를 출력할 때, FF을 두기도 하고 바로 mem으로부터 읽어가기도 한다. 아래 예제에서는 Comb. Logic으로 구성하였다

 

always_comb begin

    output_data = mem[rptr];

end

 

다섯 번째로는 Push/Pop을 하기 위한 조건인 Full/Empty 신호를 만든다. 

Empty 신호는 Read domian에서 Write Pointer와 Read Pointer가 같은 지를 판단하면 된다. 

(참고::[CDC] 09. Asynchronous FIFO(3),  https://secondspot.tistory.com/25)

if(wptr_gray_2nd == rptr_gray) Empty = 1;

else                                   Empty = 0;

 

Full 신호는 Write domain에서 Read Pointer와 Write Pointer가 최상위 2-bit은 서로 다르고, 그 이하 bit은 모두 같을 때 '1'로 만들면 된다. 

(참고::[CDC] 09. Asynchronous FIFO(3),  https://secondspot.tistory.com/25)

 

if(wptr_gray[MSB] != rptr_gray_2nd[MSB]) & (wptr_gray[MSB-1] != rptr_gray_2nd[MSB-1]) & (wptr_gray[MSB-2:0] == rptr_gray_2nd[MSB-2:0])) Full = 1;

else                                          Full = 0;



이렇게 구성을 하면 Asynchronous FIFO의 설계가 마무리된다. 위에서도 언급했듯이 가장 중요한 점은 data 자체를 CDC 기법을 사용하는 것이 아니라 Pointer에 CDC 기법을 사용한다. Pointer가 Clock Domain을 넘어가면서 data는 stable한 상태임을 보장받을 수 있다. Full/Empty 신호를 서로 살펴보므로 data가 mem에 저장되는 순간에는 절대로 읽어 가거나 쓸 수 없다. 

 

10번의 Post들을 통해서 Asynchronous FIFO를 설계하는 방법에 대해서 알아보았다. 글을 적으면서 이해가 명확하지 않았던 부분도 다시 한번 살펴보는 계기가 되었다. 이 글을 마지막으로 보시는 독자들도 내용이 부족하거나 궁금한 점이 있으면 언제든지 댓글을 남겨주시기 바랍니다.

읽어주셔서 감사합니다.

'IT_Study > ASIC_Study' 카테고리의 다른 글

[CDC] 09. Asynchronous FIFO(3)  (8) 2022.04.13
[CDC] 08. Asynchronous FIFO(2)  (2) 2022.03.31
[CDC] 07. Asynchronous FIFO(1)  (4) 2022.03.29
[CDC] 06. Gray Code vs. Binary Code(2)  (5) 2022.03.28
[CDC] 05. Gray Code vs. Binary Code(1)  (4) 2022.03.26

댓글