[CDC] 7_Multi Bit CDC: Asynchronous FIFO (First-In First Out)

2025. 12. 20. 23:55·VLSI/Design
728x90
반응형

Asynchronous FIFO를 제대로 이해하기 위해서는, 먼저 Synchronous FIFO 구조를 머릿속에 명확히 고정할 필요가 있다. 이를 위해 가장 기초적인 FIFO(First-In First-Out) 구조부터 정리한다.

 

 

FIFO (First-In First-Out)란?

 

 

먼저 들어온 데이터가 먼저 나가는 메커니즘이다. FIFO 메커니즘을 사용하는 대표적인 방식으로 큐(Queue) 자료 구조가 있다. 데이터를 순서대로 안전하게 저장하고 전달하기 위한 가장 기본적이면서도 중요한 하드웨어 구조이다.

 

FIFO는 주로 다음과 같은 상황에서 사용된다.

 

  • Producer / Consumer 구조
  • Burst 입력 → 일정 속도 출력
  • 파이프라인 stage 사이 버퍼

 

즉, 데이터 생성 속도와 소비 속도가 일치하지 않을 때 데이터 손실 없이 이를 완충(buffering)하기 위해 사용된다. 이는 고속 시스템에서 특히 중요하다.

 

 

위 이미지의 상황처럼 송신쪽과 수신쪽의 속도가 다르거나, 송신쪽에서 데이터를 전송하고 있을 때 수신쪽에서 다른 일을 해야할 때 FIFO가 사용된다. 이러한 상황은 동기 시스템에서도 발생할 수 있으며, 송신과 수신 클럭이 다를 경우 Asynchronous FIFO가 사용된다.

 

FIFO 주요 구성요소

FIFO 주요 인터페이스

신호 방향 설명
wr_en Input FIFO에 데이터를 쓰기 위한 Write Enable 신호
din Input FIFO로 입력되는 데이터 (Data Input)
rd_en Input FIFO에서 데이터를 읽기 위한 Read Enable 신호
dout Output FIFO에서 출력되는 데이터 (Data Output)
full Output FIFO가 가득 찼음을 나타냄
empty Output FIFO가 비어 있음을 나타냄

 

 

 

 

 

FIFO 주요 내부 신호

신호 설명
wr_ptr Write Pointer
rd_ptr Read Pointer

 

Write Pointer와 Read Pointer 두 개가 있으며, Write Pointer는 새 입력 데이터가 기록될 위치를 추적하고 Read Pointer 는 데이터를 읽을 위치를 추적한다. 두 포인터는 모두 클록에 동일 클럭 도메인이다.

 

 

MSB wrap bit

 

FIFO에서 empty와 full을 구분하려면 포인터에 주소 비트 + 1비트(MSB toggle) 가 반드시 필요하다. 이 비트는 같은 위치지만 다른 시간을 구분하기 위한 것이다.

 

  • 주소 비트 + wrap bit
  • empty = 완전 동일
  • full = 주소 동일 + MSB 다름
// empty
wptr == rptr
→ empty = 1

// full
wptr = 1_000
rptr = 0_000
addr 동일 + MSB 다름 → full = 1

 

 

 

FIFO 동작 방식

FIFO는 클락에 동기되어 쓰기와 읽기 동작을 수행한다.

Write 동작

write enable(wr_en) 신호가 활성화되어 있고, FIFO가 full 상태가 아닐 때, 매 클락 사이클마다 데이터가 FIFO에 기록된다.

  • 입력 데이터(din)는 현재 write pointer(wr_ptr)가 가리키는 위치에 저장된다.
  • 데이터가 정상적으로 기록되면, 다음 클락에서 wr_ptr는 1 증가한다.
  • FIFO가 full 상태일 경우, wr_en이 활성화되어 있어도 쓰기 동작은 수행되지 않는다.

Read 동작

read enable(rd_en) 신호가 활성화되어 있고, FIFO가 empty 상태가 아닐 때, 매 클락 사이클마다 데이터가 FIFO에서 읽힌다.

  • 출력 데이터(dout)는 현재 read pointer(rd_ptr)가 가리키는 위치의 값이다.
  • 데이터가 정상적으로 읽히면, 다음 클락에서 rd_ptr는 1 증가한다.
  • FIFO가 empty 상태일 경우, rd_en이 활성화되어 있어도 읽기 동작은 수행되지 않는다.

 

Code

module sync_fifo #(
    parameter int DEPTH  = 8,
    parameter int DWIDTH = 16
)(
    input  logic              clk,
    input  logic              rst_n,

    input  logic              wr_en,
    input  logic              rd_en,
    input  logic [DWIDTH-1:0] din,

    output logic [DWIDTH-1:0] dout,
    output logic              full,
    output logic              empty
);

    // --------------------------------------
    // Local parameters
    // --------------------------------------
    localparam int ADDR_W = $clog2(DEPTH);

    // --------------------------------------
    // Memory
    // --------------------------------------
    logic [DWIDTH-1:0] mem [0:DEPTH-1];

    // --------------------------------------
    // Pointers (Binary + wrap bit)
    // --------------------------------------
    logic [ADDR_W:0] wptr;   // MSB = wrap bit
    logic [ADDR_W:0] rptr;

    // --------------------------------------
    // Write logic
    // --------------------------------------
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wptr <= '0;
        end
        else if (wr_en && !full) begin
            mem[wptr[ADDR_W-1:0]] <= din;
            wptr <= wptr + 1'b1;
        end
    end

    // --------------------------------------
    // Read logic
    // --------------------------------------
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rptr <= '0;
            dout <= '0;
        end
        else if (rd_en && !empty) begin
            dout <= mem[rptr[ADDR_W-1:0]];
            rptr <= rptr + 1'b1;
        end
    end

    // --------------------------------------
    // Empty condition
    // --------------------------------------
    assign empty = (wptr == rptr);

    // --------------------------------------
    // Full condition
    // --------------------------------------
    assign full =
        (wptr[ADDR_W]     != rptr[ADDR_W]) &&   // wrap bit 다름
        (wptr[ADDR_W-1:0] == rptr[ADDR_W-1:0]); // 주소 같음

endmodule

module tb_sync_fifo;

    // --------------------------------------
    // Parameters
    // --------------------------------------
    localparam int DEPTH  = 8;
    localparam int DWIDTH = 16;

    // --------------------------------------
    // DUT signals
    // --------------------------------------
    logic              clk;
    logic              rst_n;
    logic              wr_en;
    logic              rd_en;
    logic [DWIDTH-1:0] din;
    logic [DWIDTH-1:0] dout;
    logic              full;
    logic              empty;

    // --------------------------------------
    // Clock generation
    // --------------------------------------
    initial clk = 0;
    always #5 clk = ~clk;   // 100MHz

    // --------------------------------------
    // DUT instantiation
    // --------------------------------------
    sync_fifo #(
        .DEPTH (DEPTH),
        .DWIDTH(DWIDTH)
    ) dut (
        .clk   (clk),
        .rst_n (rst_n),
        .wr_en (wr_en),
        .rd_en (rd_en),
        .din   (din),
        .dout  (dout),
        .full  (full),
        .empty (empty)
    );

    // --------------------------------------
    // Test sequence
    // --------------------------------------
    initial begin
        // Init
        rst_n = 0;
        wr_en = 0;
        rd_en = 0;
        din   = '0;

        #20;
        rst_n = 1;

        // ----------------------------------
        // 1. Write until FULL
        // ----------------------------------
        $display("\n--- WRITE PHASE ---");
        repeat (DEPTH) begin
            @(posedge clk);
            wr_en = 1;
            din   = din + 16'h1;
        end

        @(posedge clk);
        wr_en = 0;

        // Attempt write when FULL
        @(posedge clk);
        wr_en = 1;
        din   = 16'hFFFF;
        @(posedge clk);
        wr_en = 0;

        // ----------------------------------
        // 2. Read until EMPTY
        // ----------------------------------
        $display("\n--- READ PHASE ---");
        repeat (DEPTH) begin
            @(posedge clk);
            rd_en = 1;
        end

        @(posedge clk);
        rd_en = 0;

        // Attempt read when EMPTY
        @(posedge clk);
        rd_en = 1;
        @(posedge clk);
        rd_en = 0;

        // ----------------------------------
        // 3. Simultaneous Read & Write
        // ----------------------------------
        $display("\n--- SIMULTANEOUS R/W PHASE ---");
        repeat (5) begin
            @(posedge clk);
            wr_en = 1;
            rd_en = 1;
            din   = din + 16'h10;
        end

        @(posedge clk);
        wr_en = 0;
        rd_en = 0;

        // ----------------------------------
        // Finish
        // ----------------------------------
        #50;
        $finish;
    end

    // --------------------------------------
    // Monitor
    // --------------------------------------
    initial begin
        $display("time | wr rd | din     dout    | full empty");
        $monitor("%4t |  %0b  %0b | 0x%04h 0x%04h |  %0b     %0b",
                 $time, wr_en, rd_en, din, dout, full, empty);
    end

    // --------------------------------------
    // Dump waveform
    // --------------------------------------
    initial begin
        $dumpfile("sync_fifo.vcd");
        $dumpvars(0, tb_sync_fifo);
    end

endmodule

 

 

Simulation

time | wr rd | din     dout    | full empty
   0 |  0  0 | 0x0000 0x0000 |  0     1

--- WRITE PHASE ---
  25 |  1  0 | 0x0001 0x0000 |  0     0
  35 |  1  0 | 0x0002 0x0000 |  0     0
  45 |  1  0 | 0x0003 0x0000 |  0     0
  55 |  1  0 | 0x0004 0x0000 |  0     0
  65 |  1  0 | 0x0005 0x0000 |  0     0
  75 |  1  0 | 0x0006 0x0000 |  0     0
  85 |  1  0 | 0x0007 0x0000 |  0     0
  95 |  1  0 | 0x0008 0x0000 |  1     0
 105 |  0  0 | 0x0008 0x0000 |  1     0
 115 |  1  0 | 0xffff 0x0000 |  1     0

--- READ PHASE ---
 125 |  0  0 | 0xffff 0x0000 |  1     0
 135 |  0  1 | 0xffff 0x0001 |  0     0
 145 |  0  1 | 0xffff 0x0002 |  0     0
 155 |  0  1 | 0xffff 0x0003 |  0     0
 165 |  0  1 | 0xffff 0x0004 |  0     0
 175 |  0  1 | 0xffff 0x0005 |  0     0
 185 |  0  1 | 0xffff 0x0006 |  0     0
 195 |  0  1 | 0xffff 0x0007 |  0     0
 205 |  0  1 | 0xffff 0x0008 |  0     1
 215 |  0  0 | 0xffff 0x0008 |  0     1
 225 |  0  1 | 0xffff 0x0008 |  0     1

--- SIMULTANEOUS R/W PHASE ---
 235 |  0  0 | 0xffff 0x0008 |  0     1
 245 |  1  1 | 0x000f 0x0008 |  0     0
 255 |  1  1 | 0x001f 0x000f |  0     0
 265 |  1  1 | 0x002f 0x001f |  0     0
 275 |  1  1 | 0x003f 0x002f |  0     0
 285 |  1  1 | 0x004f 0x003f |  0     0
 295 |  0  0 | 0x004f 0x003f |  0     0

 

 

여기까지는 Write Pointer와 Read Pointer가 같은 클럭 도메인에 있기 때문에 포인터 비교가 즉각적이고 안전하다. 하지만 두 포인터가 서로 다른 클럭 도메인에 존재하게 되면, 이 단순한 비교는 더 이상 안전하지 않으며, 이를 해결하기 위해 Gray Code + Synchronizer가 필요해진다.

 

 

Rerference

https://www2.advantech.com/ia/newsletter/ADAMLINK/Aug2005/IO.htm

https://medium.com/@aiclab.official/fifo-design-and-implementation-tutorial-in-rtl-systemverilog-f11d4c78e3e8

 

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'VLSI > Design' 카테고리의 다른 글

[CDC] 9_Multi Bit CDC: Asynchronous FIFO (Full 판단, Pointer Jump)  (0) 2025.12.21
[CDC] 8_Multi Bit CDC: Asynchronous FIFO (Full/Empty Pessimistic Design)  (0) 2025.12.21
[CDC] 6_Multi Bit CDC: Asynchronous FIFO (Introduction)  (0) 2025.12.19
[CDC] 5_Multi Bit CDC: Gray Code  (0) 2025.12.15
[CDC] 4_Multi Bit CDC: Handshake  (1) 2025.12.15
'VLSI/Design' 카테고리의 다른 글
  • [CDC] 9_Multi Bit CDC: Asynchronous FIFO (Full 판단, Pointer Jump)
  • [CDC] 8_Multi Bit CDC: Asynchronous FIFO (Full/Empty Pessimistic Design)
  • [CDC] 6_Multi Bit CDC: Asynchronous FIFO (Introduction)
  • [CDC] 5_Multi Bit CDC: Gray Code
리미와감자
리미와감자
공부한 내용을 포스팅합니다.
    반응형
    250x250
  • 리미와감자
    리미창고
    리미와감자
  • 전체
    오늘
    어제
    • 분류 전체보기 (213)
      • Programming (92)
        • Common (6)
        • C (3)
        • C++ (16)
        • Python (35)
        • Front End (8)
        • Linux (1)
        • Script (7)
        • Data Structure (5)
        • Tool (11)
      • Computer Science (0)
      • VLSI (30)
        • Common (4)
        • Design (15)
        • SystemVerilog (9)
        • UVM (2)
        • FPGA (0)
      • Embedded System (31)
        • Arduino (2)
        • STM32 (7)
        • Embedded Recipes (22)
      • Semiconductor (11)
        • Semiconductor Device (1)
        • Display (10)
      • Algorithm (8)
        • Image Processing (8)
        • AI (0)
      • Certificate (26)
        • ADsP (26)
      • 일상생활 (15)
        • 맛집 리뷰 (4)
        • 나는 오늘 무엇을 샀나 ! (5)
        • 국내여행 (5)
        • 나들이 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 최근 글

  • 최근 댓글

  • 태그

    STM32
    data structure
    ADsP
    티스토리챌린지
    c++ 기초
    tkinter
    임베디드레시피
    Clock Domain Crossing
    파이참
    Handshake
    SystemVerilog
    assertion
    자료구조
    systemverilog assertions
    Bash
    SVA
    CDC
    openpyxl
    오블완
    아두이노
    군산가볼만한곳
    Metastability
    디더링
    Asynchronous FIFO
    BeautifulSoup4
    git
    임베디드시스템
    UVM
    arduino
    Dither
  • 링크

    • chipverify
    • vlsiverify
    • iksciting
    • 오늘은 맑음
    • verificationguide
  • hELLO· Designed By정상우.v4.10.6
리미와감자
[CDC] 7_Multi Bit CDC: Asynchronous FIFO (First-In First Out)
상단으로

티스토리툴바