이 페이지는 FIFOs에 관한 5페이지로 구성된 시리즈 중 첫 번째 페이지입니다.
개요
FPGA FIFO는 간단한 개념의 메모리 요소입니다. application logic 의 한 부분은 FIFO의 한 쪽에 데이터 워드를 씁니다. FIFO의 다른 쪽에서 application logic 의 다른 부분은 동일한 순서 (FIFO = First In First Out)로 이 단어를 읽습니다.
이 데이터는 FIFO내부에 저장됩니다. FIFO의 depth 는 저장할 수 있는 데이터 단어 수입니다. 너비(즉, 워드당 비트 수)와 depth는 사용자가 각 FIFO에 대해 구성하는 매개변수입니다.
FIFOs는 아마도 FPGA designs에서 가장 일반적으로 사용되는 IP 일 것입니다. logic 의 한 부분이 데이터를 생성하고 다른 부분이 데이터를 소비할 때마다 생각나는 즉각적인 솔루션은 그 사이에 FIFO를 배치하는 것입니다(물론 FIFO가 항상 올바른 솔루션이라는 것은 아닙니다...).
command-line 인터페이스(특히 UNIX / Linux)에 익숙한 사람이라면 FIFOs를 pipes 와 commands사이에서 비교할 수 있습니다. 한 프로그램의 출력이 다른 프로그램의 입력이 되고 그 사이의 마법 기계가 나머지를 처리합니다.
유비쿼터스 사용으로 인해 FPGA FIFO가 어떻게 작동해야 하는지에 대한 사실상의 합의가 있습니다. 모든 FPGA 개발 소프트웨어는 응용 프로그램의 design에서 사용할 FIFO IP module을 생성하는 방법을 제공합니다. 뿐만 아니라 이 FIFO module 에는 다른 FPGA FIFO와 마찬가지로 동작하는 특정 ports 세트가 있을 가능성이 매우 높습니다.
FPGA의 공급업체에서 제공하는 소프트웨어를 사용하면 특정 요구 사항에 맞는 FIFOs를 만들 수 있습니다. 일부 GUI 도구(폭, depth 및 기타 속성)에서 속성을 구성하기만 하면 되며 나머지는 도구에서 처리합니다. 당신에게 남은 것은 design에서 module 의 instantiation을 만드는 것뿐입니다. FPGA 세계의 다른 많은 작업과 달리 이 작업은 정말 간단합니다.
각 FPGA 공급업체는 고유한 FIFO IP를 제공하므로 문서를 작은 글씨까지 읽는 것이 중요합니다. 다른 FPGA 공급업체는 FIFOs를 설명하기 위해 약간 다른 용어를 사용합니다. module의 ports 이름도 약간 다릅니다. 또한 각 공급업체는 약간 다른 추가 기능 및 구성 옵션 세트를 제공합니다.
즉, 기본 설정이 "baseline FIFO"라고 하는 것과 일치할 가능성이 있습니다. 그 외에도 항상 사용할 수 있는 추가 기능 세트가 있습니다.
그러나 logic fabric 에서 FIFO를 구현하는 것은 공급업체마다 다르므로 FIFO속성의 의미를 이해하는 것은 FPGA의 리소스를 잘 활용하는 데 중요합니다.
대체로 FPGA FIFOs를 알고 이해하는 것은 일회성 노력입니다. 하나의 FPGA에서 작업하는 방법을 배운 후에는 다른 FPGA에서도 동일한 작업을 수행하는 것이 매우 쉽습니다. 그 자체로 그들의 편재성에 대한 이유입니다.
baseline FIFO
FPGA FIFOs에 대한 문서화된 표준은 없지만 그럼에도 불구하고 어떻게 동작해야 하는지에 대한 광범위한 동의가 있습니다.
모든 FIFOs 에는 두 개의 인터페이스가 있습니다. 하나는 단어 쓰기용이고 다른 하나는 단어 읽기용입니다. "baseline FIFO"라고 부를 instantiation을 살펴보겠습니다. 여기에는 중요한 변형이 있으며 나중에 설명하겠습니다.
myfifo myfifo_ins
(
.rst(rst), // Asynchronous reset input
// Write interface ports
.wr_clk(wr_clk), // Write clock input
.wr_en(wr_en), // Write Enable input
.din(din), // Write word input
.full(full), // Full output
// Read interface ports
.rd_clk(rd_clk), // Read Clock input
.rd_en(rd_en), // Read Enable input
.dout(dout), // Read word output
.empty(empty) // Empty output
);
ports 의 이름은 Xilinx의 도구에서 사용하는 이름이지만 다른 FPGA 공급업체도 비슷한 이름을 사용합니다.
FIFO module의 ports는 세 그룹으로 나뉩니다. reset signal (@rst)는 나중에 다시 설명하겠습니다. 예상대로 쓰기 인터페이스와 읽기 인터페이스가 있는데, 각각 4개의 ports로 구성되어 있습니다.
@din 및 @dout와 관련하여 FIFO 로 들어오고 나가는 데이터 워드를 전달하는 두 개의 vector ports 입니다. 이 단어의 폭은 관련 소프트웨어 도구를 사용하여 FIFO를 설정할 때 결정하는 것입니다. 또한 FIFO의 depth , 즉 포함할 수 있는 단어 수를 설정합니다. 이 두 매개변수는 FIFO가 소비하는 FPGA의 메모리 리소스 양에 영향을 줍니다.
Clocks
이 두 인터페이스 각각에는 고유한 clock이 있습니다. @wr_clk 및 @rd_clk. 각 인터페이스의 다른 ports는 이 두 clocks각각과 동기화됩니다.
FIFOs는 두 개의 clocks가 있기 때문에 한 clock domain 에서 다른 clock domain 로 데이터를 이동하는 데 자주 사용됩니다. design 의 일부 logic이 clk_A와 동기화되고 다른 부분이 clk_B와 동기화되는 경우 어떻게 함께 작동하게 합니까? 모든 FPGA engineer 의 첫 번째 생각은 그 사이에 FIFO를 넣는 것입니다. 이는 주로 crossing clock domains 의 작업이 큰 골칫거리이고 FIFO를 사용하면 문제를 쉽고 안전하게 해결하기 때문입니다.
쓰기 인터페이스
쓰기 인터페이스는 간단합니다. @wr_clk, @wr_en , @din은 inputs ~ FIFO, @full은 output입니다.
@wr_en이 @wr_clk의 rising edge 에서 하이일 때, @din 의 데이터는 FIFO로 푸시됩니다. FIFO가 가득 차면 @full 포트가 하이입니다.
예를 들어, 이것은 FIFO에 5개의 단어를 쓰는 waveform 입니다.
이 waveform에서 application logic은 먼저 D0 및 D1라는 단어를 씁니다. FIFO는 @full output을 올려 D1쓰기에 성공한 후 FIFO가 가득 찼음을 알립니다. application logic 는 동일한 clock cycle동안 @wr_en을 낮추어 이에 대응합니다. 몇 번의 clock cycles후에 FIFO는 @full을 로우로 변경하여 다시 쓸 수 있음을 나타냅니다. 이것은 다른 쪽의 활동 때문일 가능성이 가장 높습니다(즉, FIFO에서 데이터를 읽었습니다).
application logic은 @full이 낮음으로 변경된 clock cycle 에서 쓰기를 시작할 수 있었지만 약간 나중에(이 특정 예에서) 쓰기 시작합니다. waveform와 같이 3개의 단어가 추가로 쓰여집니다.
waveform에서 @din이 "Dx" 값으로 표시되는 경우 값이 무시되므로 어떤 값이 있는지는 중요하지 않습니다. 예를 들어, D1 와 D2사이에 "Dx"가 있는 세그먼트에서 @din은 D1에 남아 있거나 표시된 것보다 일찍 D2 로 변경되거나 완전히 다른 것으로 변경될 수 있습니다. 결과는 같았을 것입니다.
간단한 코딩 예를 들어, 가능할 때마다 FIFO를 카운트업하는 단어로 채우고 싶다고 가정해 보겠습니다.
assign wr_en = !full;
always @(posedge wr_clk)
if (wr_en)
din <= din + 1;
이것은 @full 와 @wr_en사이의 올바른 관계를 예시합니다. @full이 높으면 @wr_en은 같은 clock cycle에서 낮아야 합니다. 그렇지 않다면 어떨까요? @full signal을 무시한다면 어떨까요? 이 경우 FIFO가 wr_en을 무시할 가능성이 큽니다. 따라서 wr_en port가 다음과 같이 정의된 @the_real_wr_en에 연결된 것처럼 동작할 것입니다.
assign the_real_wr_en = wr_en && !full;
그러나 일부 FPGA 도구에서는 이 안전 메커니즘 없이 FIFO를 구성할 수 있습니다. 그렇다면 FIFO가 가득 찼을 때 데이터를 쓰려고 하면 거의 모든 일이 발생할 수 있습니다.
이런 식으로 @full을 존중해야 합니다. 그렇지 않으면 데이터가 유출된 것처럼 보입니다. 위의 예를 고려하십시오. @wr_en이 항상 하이였다면 @din은 데이터가 FIFO 에 기록되었는지 여부에 관계없이 계속 카운트업했을 것입니다. 따라서 다른 쪽에서 데이터를 읽을 때 카운트업이 불연속적이었을 것입니다.
@full은 write cycle의 결과로만, 즉 @wr_en이 하이일 때 rising clock edge 직후에 로우에서 하이로 변경될 수 있습니다. FIFO가 재설정되는 경우를 제외하고 아래에서 자세히 설명합니다.
읽기 인터페이스
읽기 인터페이스는 매우 유사하지만 완전히 동일하지는 않습니다. @rd_clk 및 @rd_en은 inputs ~ FIFO, @dout 및 @empty는 outputs입니다.
@rd_en이 @rd_clk의 rising edge 에서 하이이면 FIFO의 메모리에서 새 단어를 읽고 @dout는 해당 rising edge이후 의 값으로 업데이트됩니다. 즉, 다음 clock cycle에서. @empty port는 FIFO가 비어 있을 때 하이입니다.
이 예 waveform에서 FIFO에서 5개의 단어를 읽습니다.
이 waveform에서 application logic은 세 단어를 읽는 것으로 시작합니다. @empty가 high로 변경되면( D2가 @dout에 표시됨) application logic은 동일한 clock cycle에서 @rd_en을 low로 변경합니다. 이전과 마찬가지로 @empty가 로우로 변경된 동일한 clock cycle 에서 @rd_en을 다시 하이로 변경하는 것이 좋았을 것입니다(다른 쪽의 FIFO 에 데이터가 기록되기 때문에). 대신 몇 개의 clock cycles를 기다렸다가 두 단어를 추가로 읽었습니다.
그리고 이제 사소한 참고 사항 : 이 waveform을 위의 waveform 와 비교하면 5단어가 쓰여지고 5단어가 읽히는 것을 알 수 있습니다. 그렇다면 D4가 등장하면서 @empty가 상승하지 않은 이유는 무엇입니까? 글쎄요, FIFO가 비어 있지 않아도 읽기를 멈추어도 괜찮다는 것을 보여주고 싶었기 때문입니다. 따라서 이 가상의 예를 위해 FIFO에 추가 단어가 작성되었으므로 FIFO는 D4를 읽은 후 비어 있지 않았습니다.
@dout는 @rd_en이 낮을 때 값을 유지합니다. application logic은 이것에 의존할 수 있습니다: @dout는 항상 FIFO 에서 읽은 마지막 단어의 값을 포함합니다( reset이후 제외).
더 중요한 것은 @rd_en이 하이일 때 @dout 의 새로운 값이 rising edge 다음 에 나타납니다. 따라서 FIFO는 다음 Verilog 코드처럼 작동합니다.
always @(posedge rd_clk)
if (rd_en && !empty)
dout <= next_word_to_show;
이 가짜 Verilog 코드는 @empty가 동일한 clock cycle에서 높을 때 대부분의 FIFOs가 @rd_en을 무시한다는 사실을 보여줍니다. 쓰기 인터페이스와 마찬가지로 @empty가 해당 clock cycle에서 높으면 @rd_en이 높으면 안 됩니다. 다시 한 번, 때때로 FIFO가 이 보호 메커니즘을 갖지 않도록 구성될 수 있으므로 이 규칙을 어기지 마십시오.
@empty는 read cycle이후, 즉 clock의 rising edge 에서 @rd_en이 하이인 후에만 로우에서 하이로 변경할 수 있습니다. 유일한 예외는 FIFO가 재설정되는 경우입니다.
예를 들어 FIFO에서 단어를 읽고 누적 합계를 계산하는 단순화된 Verilog 코드 조각( reset제외)이 있습니다.
assign rd_en = want_to_read_now && !empty;
always @(posedge rd_clk)
begin
rd_en_d <= rd_en;
if (rd_en_d)
sum <= sum + dout; // Don't try this at home: @sum is never reset.
end
데모를 위해 @want_to_read_now signal을 추가했는데, 이는 logic이 읽고 싶어한다는 것을 나타냅니다. 그럼에도 불구하고 @rd_en은 FIFO가 비어 있지 않은 경우에만 높습니다.
하나의 clock cycle지연과 함께 @rd_en 의 값을 포함하는 @rd_en_d에 주의하십시오. 결과적으로 @rd_en_d는 @dout에 새롭고 유효한 값이 있을 때와 동시에 하이입니다. 이것이 @rd_en_d가 @dout의 가치를 소비하기 위한 조건으로 사용되는 이유입니다. @rd_en 와 @dout 사이의 지연은 이 예에서 보여주듯이 상황을 약간 어렵게 만듭니다.
동기화 및 latency
쓰기와 읽기를 위해 위의 waveforms 예제를 별도로 그렸기 때문에 중요한 점을 놓치고 있습니다. 비어 있는 FIFO 에 첫 번째 단어를 쓰는 데 몇 clock cycles가 걸리고 @empty port가 낮게 바뀔 때까지 걸립니다. 마찬가지로, 가득 찬 FIFO 에서 첫 번째 단어를 읽는 데 몇 clock cycles가 걸리고 @full port가 낮게 바뀔 때까지 걸립니다.
이것은 FIFO 에 쓰기에 대한 정보가 이 정보가 FIFO의 다른 쪽에 도달하기 전에 두 clock domains에 전파되어야 하기 때문에 발생합니다. clock domains를 가로지르는 데 필요한 logic 로 인해 몇 개의 clock cycles가 지연됩니다. 따라서 @empty port는 약간 늦게 응답합니다. @full port도 마찬가지입니다.
그렇다면 이 지연은 몇 개의 clock cycles 입니까? 그것은 특정 순간에 두 clocks의 edges 사이의 시간 관계를 포함하여 많은 것에 달려 있습니다. 한마디로 말하기 어렵다.
이 지연에 영향을 주는 것들 중에는 synchronization stages의 수가 있는데, 이는 종종 FIFO에 대해 설정할 수 있는 매개변수입니다. 2개의 stages가 일반적인 선택이지만 더 큰 수를 선택할 수도 있습니다. 이것은 logic의 리소스를 조금 더 사용하는 대신 FIFO의 안정성을 높이는 데 도움이 될 수 있습니다. 또한 방금 논의한 것처럼 @empty port 및 @full port의 latency 도 증가합니다.
따라서 FIFO에 탐닉하고 싶다면 synchronization stages를 3개로 늘려 절대적으로 안전하다고 느끼십시오.
reset input
모든 FPGA FIFOs 에는 reset signal이 있습니다. FIFO는 두 개의 clocks를 사용하므로 이 reset signal은 어느 것과도 동기화되지 않을 것으로 예상되므로 비동기적입니다. FIFO의 내부 logic은 두 개의 clock domains각각에 대해 reset을 내부적으로 동기화합니다.
그렇다면 reset은 무엇을 합니까? 먼저 FIFO를 비우고 @empty를 높음으로 설정합니다. FIFO에 데이터가 있는 경우 이 데이터는 손실됩니다.
@full output의 경우 FIFO가 데이터를 수신할 준비가 될 때까지(즉, write cycles사용) FIFOs가 reset의 결과로 이 output을 높게 유지하는 것이 일반적이고 권장됩니다. 그러나 이 동작은 선택 사항일 수 있으므로 FIFO설명서에서 이 항목을 확인하는 것이 좋습니다. 결국, FIFO는 reset이후에 실제로 가득 차 있지 않습니다. 또한 reset 때문에 @full을 높음으로 변경하면 위에서 언급한 규칙이 위반됩니다. @full은 데이터 쓰기의 결과로만 high로 변경되어야 합니다.
reset signal이 활성화되는 순간부터 @empty port 와 @full port가 하이로 전환될 때까지 몇 개의 clocks cycles가 걸린다는 점을 알아두는 것이 중요합니다. 이는 FIFO의 synchronization logic때문입니다. 따라서 reset활성화 주변의 몇 개의 clock cycles 동안은 상황이 약간 모호합니다. application logic이 reset주변의 몇 개의 clock cycles 동안 FIFO 에서 쓰거나 읽지 않으려고 하지 않는지 확인하세요.
reset signal이 비동기적이더라도 FPGA의 register (flip-flop)의 output이 되어야 합니다. reset은 combinatorial logic의 output이 되어서는 안 됩니다. 왜냐하면 FIFO가 glitches의 결과로 의도치 않은 resets를 받을 수 있기 때문입니다.
사실, 많은 FPGA engineers는 reset port 에 사실상 무엇이든 연결하면 작동할 것이라고 잘못 가정합니다. 그러나 FPGA 공급업체는 reset signal에 예상치 못한 사양을 가지고 있을 수 있습니다. 예를 들어, 이는 Xilinx의 FIFO (PG057) 제품 가이드 에서 발췌한 것입니다.
If the asynchronous reset is one slowest clock wide and the assertion happens very close to the rising edge of slowest clock, then the reset detection may not happen properly causing unexpected behavior. To avoid such situations, it is always recommended to have the asynchronous reset asserted for at least 3 [ ... ] slowest clock cycles...
(Chapter 3, "Resets")
따라서 Xilinx는 reset이 최소 3개의 clock cycles에 대해 활성화되도록 권장합니다. 이 권장 사항을 알고 있는 사람이 얼마나 될지 잘 모르겠습니다. 반드시 공급업체의 FIFO 사용자 가이드를 읽어 이 reset signal을 올바르게 생성하는 방법을 알아보세요.
FIFO 구현 방법
공급업체의 소프트웨어 도구가 FIFO가 제대로 작동하도록 모든 것을 처리하지만, 특히 특정 유형의 리소스 부족을 방지하기 위해 FPGA 의 어떤 리소스가 활용되는지 알고 있는 것이 좋습니다.
각 FPGA 에는 옵션이 있지만 몇 가지 일반적인 옵션을 간단히 언급하겠습니다.
- 하드웨어에서 완전히 구현됩니다. 이는 일반적으로 block RAM이 스토리지에 사용된다는 것을 의미하며, 그 위에 FIFO를 제어하는 logic이 logic fabric이 아닌 silicon 에 구현되어 있습니다. 이것은 logic을 많이 저장하지 않지만 silicon 의 구현은 아마도 고주파수에 더 나을 것입니다. 주요 단점은 이러한 FIFO 의 기능 세트가 silicon에서 구현되는 것으로 제한되어 FIFO 의 크기가 상대적으로 제한될 수 있고 사소한 기능이 누락될 수 있다는 것입니다.
- Block RAM FIFOs. 이것은 가장 일반적인 종류입니다. FIFO는 FIFO의 너비와 depth를 얻기 위해 필요한 block RAMs 의 양으로 구성됩니다. FIFO를 제어하는 logic은 logic fabric에 구현되어 있습니다.
- Distributed RAM FIFOs. 이것은 Block RAM FIFOs와 비슷하지만 logic slices는 block RAMs대신 RAM 로 사용됩니다. 대부분의 FPGAs 에는 slice의 LUTs를 RAMs로 사용할 수 있는 기능이 있으므로 특히 FIFO가 얕을 때 적합한 옵션입니다. 따라서 이 옵션은 일반적으로 depth가 32단어 이하인 경우에 적합하지만 design 및 FPGA 제품군에 따라 장단점이 다릅니다.
- shift registers기반FIFOs . 이것은 분산된 RAM FIFOs의 이국적인 버전입니다: slices 도 shift registers처럼 동작할 수 있기 때문에 이를 활용하여 logic을 조금 절약할 수 있습니다.
이것으로 FIFOs에 대한 이 시리즈 의 첫 페이지를 마칩니다. 다음 페이지 에서는 FIFOs 의 일반적인 변형과 추가 기능에 대해 설명합니다.