이 페이지는 FIFOs에 대한 5페이지 시리즈 중 네 번째입니다. 이 페이지는 overflow가 발생할 가능성에 대처하는 방법을 제안합니다.
소개
FIFO를 사용하는 많은 애플리케이션에서는 도착하는 데이터의 흐름을 제어할 가능성이 없습니다. 예를 들어, data acquisition 애플리케이션에서 logic은 캡처된 데이터를 FIFO에 직접 씁니다. FIFO가 가득 차면 이 데이터가 손실됩니다. 이런 종류의 애플리케이션에서는 FIFO 의 리더가 FIFO 의 데이터를 충분히 빠르게 소비하여 FIFO가 가득 차지 않도록 해야 합니다.
그러나 FIFO가 가득 차면 데이터 손실로 인해 데이터 연속성이 깨집니다. FIFO는 쓰기 시도를 무시합니다. 결과적으로 FIFO 의 내용이 올바르지 않습니다. 이러한 상황을 overflow라고 합니다.
overflow는 일종의 오작동으로 인한 결과이므로 피해야 합니다. 그러나 overflow가 발생하더라도 이 이벤트를 감지하고 피해를 완화하는 것이 중요합니다. 이에 대한 전략은 아래와 같습니다.
수정된 FIFO
주된 노력은 항상 overflow 발생을 방지하는 것이어야 합니다. 그러나 이것이 실패하면 남은 것은 FIFO에서 잘못된 데이터가 소비되지 않도록 하는 것뿐입니다. 즉, FIFO 에서 읽는 모든 데이터는 연속적이고 정확합니다.
제안된 솔루션은 수정된 FIFO가 며 두 가지 측면에서 다릅니다.
- overflow가 발생하면(예: FIFO가 가득 차면) FIFO가 재설정될 때까지 FIFO 에 더 이상 데이터를 쓸 수 없습니다.
- overflow이후 FIFO가 비어 있을 때, FIFO는 reset이 발생할 때까지 비어 있을 것이라고 보고합니다. 이 상황을 EOF라고 부르겠습니다.
첫 번째 기능은 수정된 FIFO 에서 읽은 데이터가 항상 연속적이고 정확하다는 것을 보장합니다. FIFO는 연속성이 깨진 후 데이터 쓰기를 거부합니다. 두 번째 기능(EOF)은 FIFO를 사용하는 logic 에 문제가 발생했다는 메시지를 보냅니다. 이는 logic 에 데이터 흐름을 다시 시작할 수 있는 기회를 제공합니다.
EOF 라는 이름은 End of File에서 유래되었습니다. 이 수정된 FIFO는 Xillybus IP core를 기반으로 하는data acquisition 응용 프로그램 에 유용합니다. 이런 종류의 응용 프로그램에서 Xillybus IP core는 데이터를 컴퓨터로 전송합니다. 간단한 컴퓨터 프로그램은 FIFO에서 데이터를 읽기 위해 표준 file I/O API를 사용합니다. 즉, 컴퓨터 프로그램은 일반적인 방법으로 파일을 열고 그 파일에서 데이터를 읽습니다. FIFO의 EOF는 이 파일이 일반 파일의 끝에 도달했을 때와 유사하게 작동하도록 만듭니다. 이는 더 이상 읽을 데이터가 없다는 사실에 대한 자연스러운 반응입니다.
Verilog의 구현
다음은 위에서 제안한 수정된 FIFO를 구현하는 Verilog module 의 예입니다.
module eof_fifo
(
input rst,
input wr_clk,
input rd_clk,
input [31:0] din,
input wr_en,
input rd_en,
output [31:0] dout,
output full,
output empty,
output eof
);
reg rst_sync;
reg rst_cross;
reg fifo_has_been_full;
reg fifo_has_been_nonfull;
reg has_been_full_cross;
reg has_been_full;
assign ok_to_write = !rst_sync && !full && !fifo_has_been_full;
assign eof = empty && has_been_full;
always @(posedge wr_clk)
begin
if (!full)
fifo_has_been_nonfull <= 1;
else if (rst_sync)
fifo_has_been_nonfull <= 0;
if (full && fifo_has_been_nonfull)
fifo_has_been_full <= 1;
else if (rst_sync)
fifo_has_been_full <= 0;
end
// Clock domain crossing logic: asynchronous -> wr_clk
always @(posedge wr_clk)
begin
rst_cross <= rst;
rst_sync <= rst_cross;
end
// Clock domain crossing logic: wr_clk -> rd_clk
always @(posedge rd_clk)
begin
has_been_full_cross <= fifo_has_been_full;
has_been_full <= has_been_full_cross;
end
fifo fifo_ins
(
.rst(rst),
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.din(din),
.wr_en(wr_en && ok_to_write),
.rd_en(rd_en),
.dout(dout),
.full(full),
.empty(empty)
);
endmodule
이 module은 standard FIFO의 instantiation 와 약간의 추가 logic로 구성되어 있다는 것이 매우 분명합니다. 이 module의 ports는 standard FIFO의 ports 와 거의 정확히 동일하다는 점에 유의하세요. 단 하나의 차이점이 있습니다. 수정된 FIFO 에는 @eof라는 이름의 port가 있습니다.
또한 @din 및 @dout 의 너비는 32 bits입니다. 폭이 다른 FIFO를 원하는 경우 module상단에 ports를 선언하는 것만 변경하면 됩니다.
수정된 FIFO 에는 @full port가 있는데 꼭 유용하지는 않습니다. FIFO 에 쓰는 logic은 이 port를 무시할 수도 있습니다. FIFO가 가득 차면 어차피 어쩔 수 없습니다. 이런 식으로 FIFO는 나중에 데이터를 쓰려는 시도를 무시합니다. 대부분의 응용 프로그램에서 FIFO가 가득 찼다는 지식은 별로 도움이 되지 않습니다. @eof가 높아질 때까지 기다린 다음 전체 메커니즘을 다시 시작하는 것이 가장 좋습니다.
overflow이후 쓰기 작업 방지
이미 언급했듯이, 이 module은 standard FIFO를 기반으로 합니다. 이 FIFO의 모든 ports는 eof_fifo의 ports에 직접 연결되어 있으며, 단 하나의 port는 예외입니다. @wr_en. 이 port는 대신 "wr_en && ok_to_write"에 연결됩니다. 따라서 FIFO가 가득 찬 후 쓰기 작업을 중지하는 데 @ok_to_write가 사용된다는 것은 매우 분명합니다. 이 wire 의 정의는 다음과 같습니다.
assign ok_to_write = !rst_sync && !full && !fifo_has_been_full;
이 표현을 통해 쓰기 작업을 방해하는 세 가지 상황이 있음을 알 수 있습니다.
- FIFO가 재설정되는 경우
- FIFO가 가득 차면
- 과거에 FIFO가 꽉 찼을 때
첫 번째 두 가지 조건은 사소합니다. @fifo_has_been_full로 표현되는 세 번째 조건에 초점을 맞춰 보겠습니다.
always @(posedge wr_clk)
begin
if (!full)
fifo_has_been_nonfull <= 1;
else if (rst_sync)
fifo_has_been_nonfull <= 0;
if (full && fifo_has_been_nonfull)
fifo_has_been_full <= 1;
else if (rst_sync)
fifo_has_been_full <= 0;
end
FIFO가 가득 차면@fifo_has_been_full이 높아집니다. 이 register는 @full 와 @fifo_has_been_nonfull이 모두 High일 때 High로 변경됩니다.
첫 번째 부분은 놀라운 일이 아닙니다. @full은 FIFO의 "full" port에 연결됩니다. 그런데 @fifo_has_been_nonfull이 왜 필요한가요? 그 이유는 FIFO가 reset 상태로 유지되는 한 FIFO가 "full"를 높게 유지하는 경우가 많기 때문입니다. 이 기능의 목적은 FIFO가 아직 데이터를 수신할 준비가 되지 않았음을 application logic 에 알리는 것입니다. @fifo_has_been_nonfull 의 목적은 이 시나리오에서 @fifo_has_been_full이 실수로 높아지는 것을 방지하는 것입니다.
FIFO가 재설정되면@fifo_has_been_full은 low로 변경됩니다. 즉, FIFO를 재설정하는 것은 overflow가 발생한 후 수정된 FIFO 로 정상적인 작동을 재개할 수 있는 유일한 방법입니다. @rst는 asynchronous reset입니다. 따라서 올바른 clock domain 에 속하는 reset signal을 생성하려면 logic을 추가해야 합니다. 이 신호는 @rst의 복사본인 @rst_sync입니다.
EOF생성 중
@eof port는 다음 두 가지 조건이 모두 충족되면 높아집니다.
- FIFO 의 모든 데이터가 소비되었습니다. FIFO는 비어 있습니다.
- 더 이상 FIFO에 데이터가 기록되지 않습니다. @fifo_has_been_full은 과거에 FIFO가 꽉 찼기 때문에 높습니다. 따라서 @ok_to_write는 FIFO가 재설정될 때까지 낮게 유지됩니다.
이러한 조건에 대한 Verilog 코드의 표현식은 다음과 같습니다.
assign eof = empty && has_been_full;
이 표현식은 @fifo_has_been_nonfull이 아닌 @has_been_full을 기반으로 합니다. @eof 와 @empty는 모두 @rd_clk의 clock domain에 속합니다. 하지만 @fifo_has_been_nonfull은 @wr_clk의 clock domain에 속합니다. 따라서 @fifo_has_been_nonfull은 clock domain crossing덕분에 @rd_clk 의 clock domain 로 복사됩니다. 이 사본은 @has_been_full입니다.
따라서 @eof는 " FIFO가 비어 있을 뿐만 아니라 FIFO를 재설정할 때까지 채워지지 않습니다"를 의미합니다.
결론
많은 응용 프로그램에서 overflow가 발생하지 않는다고 보장하는 것은 불가능합니다. 이 사건이 용납할 수 없는 경우 발생 시 피해를 줄이는 것이 가능합니다. 전략은 overflow까지 기록된 데이터를 통과시키고 더 이상 데이터가 기록되지 않도록 하는 것입니다. 결국 FIFO는 비어 있게 될 것입니다. 그런 일이 발생하면 FIFO는 @eof port덕분에 더 이상 데이터가 도착하지 않을 것이라고 보고합니다.
이 방법은 FIFO에서 읽는 데이터의 연속성을 보장합니다. FIFO가 가득 차서 중간에 데이터가 손실되지 않았습니다. 따라서 이 방법은 data acquisition 응용 프로그램에 유용합니다.
이 페이지에서는 이 전략을 기반으로 하는 수정된 FIFO를 구현하는 방법을 보여주었습니다. 이 수정된 FIFO는 standard FIFO의 drop-in replacement 로 사용할 수 있습니다. 유일한 중요한 차이점은 FIFO를 사용하는 logic이 @eof port에 주의를 기울여야 한다는 것입니다. 이 port가 하이가 되면 logic은 FIFO를 재설정하고 데이터 흐름을 다시 시작해야 합니다.
이것으로 FIFOs에 관한 이 시리즈 의 네 번째 페이지를 마무리합니다. 다음 페이지에서는 "standard FIFO"를 FWFT FIFO 로 또는 그 반대로 바꾸는 방법과 timing을 개선하는 방법을 보여줍니다.