01signal.com

registers를 추가하여 FIFOs 에서 timing 개선

개요

FIFOs에 대한 시리즈 의 마지막인 이 페이지는 기존 FIFO를 다른 것으로 수정하는 방법을 보여줍니다. 먼저 "standard FIFO"를 FWFT FIFO로 바꾸고 그 반대로 하는 방법입니다. 이것은 FIFO의 timing을 개선하기 위한 몇 가지 고급 방법, 즉 더 높은 주파수에서 작동하도록 합니다.

실용적인 관점에서, timing constraints를 달성하는 데 FIFO 와 관련된 문제가 없는 한 이 페이지를 읽는 것은 매우 무의미합니다. 이 페이지의 내용은 어렵고 FIFO의 일반적인 사용에는 필요하지 않습니다. 그럼에도 불구하고 logic, 특히 데이터를 처리하는 logic을 설계하는 동안 사용할 근육을 훈련하기 위한 목적으로 운동으로서 노력할 가치가 있습니다.

직접적인 관련은 없지만 외부 메모리의 도움으로 매우 깊은 FIFO를 생성하는 방법을 보여주는 또 다른 페이지 가 있습니다(일반적으로 DDR memory가 지만 AXI 인터페이스로 래핑된 모든 것이 가능함). 이 트릭의 좋은 점은 이 거대한 FIFO가 사용하는 외부 메모리만큼 깊을 수 있지만 이 모든 것이 application logic에 투명하다는 것입니다. baseline FIFO와 동일한 인터페이스를 가지고 있습니다.

나는 몇 년 전에 이 페이지에서 Verilog 코드를 작성했기 때문에 코딩 스타일이 오늘날과 약간 다르다는 것을 언급해야 합니다.

Standard FIFO ~ FWFT FIFO

이전 페이지 에서 FWFT FIFOs 에 대해 빠르게 요약하면 다음과 같습니다. "standard FIFO"의 경우 낮은 @empty port는 rising clock edge에서 @rd_en이 높은 후 FIFO의 output 에 유효한 데이터가 표시됨을 의미합니다. FWFT FIFO는 사용 가능한 즉시 output 의 데이터를 제공하므로 낮은 @empty 신호는 output 의 데이터가 유효 함을 의미합니다.

@rd_en 의 의미도 다릅니다. "standard FIFO"의 경우 "데이터 가져오기"를 의미합니다. FWFT FIFO 에서는 "방금 데이터를 사용했습니다. 다음 데이터 가 있으면 가져오세요."와 같습니다.

그래서 이것은 "standard FIFO"를 FWFT FIFO로 바꾸는 module 입니다. 당연히 @rd_en 및 @empty만 조작합니다. 나머지 신호는 그냥 통과됩니다.

module basic_fwft_fifo(rst,
                       rd_clk, rd_en, dout, empty,
                       wr_clk, wr_en, din, full);

   parameter width = 8;

   input                 rst;
   input                 rd_clk;
   input                 rd_en;
   input                 wr_clk;
   input                 wr_en;
   input [(width-1):0]   din;
   output                empty;
   output                full;
   output [(width-1):0]  dout;

   reg                   dout_valid;
   wire                  fifo_rd_en, fifo_empty;

   // orig_fifo is just a normal (non-FWFT) synchronous or asynchronous FIFO
   fifo orig_fifo
      (
       .rst(rst),
       .rd_clk(rd_clk),
       .rd_en(fifo_rd_en),
       .dout(dout),
       .empty(fifo_empty),
       .wr_clk(wr_clk),
       .wr_en(wr_en),
       .din(din),
       .full(full)
       );

   assign fifo_rd_en = !fifo_empty && (!dout_valid || rd_en);
   assign empty = !dout_valid;

   always @(posedge rd_clk or posedge rst)
      if (rst)
         dout_valid <= 0;
      else
         begin
            if (fifo_rd_en)
               dout_valid <= 1;
            else if (rd_en)
               dout_valid <= 0;
         end
endmodule

일반적으로 여기에서 코드를 설명하지만 이전 페이지 에서 FWFT FIFO 에 대해 제공된 설명을 반복합니다.

FWFT FIFO ~ standard FIFO

이것은 정말 간단합니다. FWFT FIFO 의 낮은 @empty는 데이터가 output port에 있음을 의미하므로 @rd_en이 높을 때 이 데이터를 샘플링하는 register를 만듭니다.

그래서 그것은 바로 이것입니다:

module standard_fifo(rst,
                     rd_clk, rd_en, dout, empty,
                     wr_clk, wr_en, din, full);

   parameter width = 8;

   input                 rst;
   input                 rd_clk;
   input                 rd_en;
   input                 wr_clk;
   input                 wr_en;
   input [(width-1):0]   din;
   output                empty;
   output                full;
   output [(width-1):0]  dout;

   reg [(width-1):0]     dout;
   wire [(width-1):0]    dout_w;

   always @(posedge rd_clk)
     if (rd_en && !empty)
       dout <= dout_w;

   fwft_fifo wrapper
     (
      .wr_clk(wr_clk),
      .rd_clk(rd_clk),
      .rst(rst),
      .din(din),
      .wr_en(wr_en),
      .rd_en(rd_en && !empty),
      .dout(dout_w),
      .full(full),
      .empty(empty)
      );
endmodule

@dout 만 조작된다는 점에 유의하십시오. @empty는 다음과 같이 전달됩니다. 높으면 @dout_w가 유효하지 않으므로 @dout는 값을 샘플링할 수 없습니다.

timing개선 요령

FIFOs에 대한 이 네 페이지의 최종 결승전에 오신 것을 환영합니다. 확실히 가장 읽기 어려운 부분입니다.

그래서 때때로 FPGA design이 timing constraints 에 도달하지 못하는 이유(즉, 원하는 clock frequency에 도달)를 알아내려고 할 때 critical path가 FIFO에서 시작 및/또는 끝나는 것으로 나타났습니다. 먼저 풀기 쉬운 케이스를 짚고 하드너트로 마무리하자.

@empty 및/또는 @full이 critical path에 있는 경우

@empty 신호 및 @full 신호는 특히 @wr_en 및 @rd_en이 이들 중 combinatorial functions 인 경우 critical path에 나타날 수 있습니다. 이는 주로 이러한 신호가 FIFO에서 쓰기 작업 또는 읽기 작업을 요청하기 위한 것이 아니라 데이터를 소비하거나 생성하는 application logic 용 enable signals 로도 작동하기 때문입니다. 데이터가 흐르지 않으면 logic 도 멈춥니다.

따라서 @wr_en 및 @rd_en에 의존하는 logic equations가 많이 있으며 logic functions가 상당히 복잡한 경우가 많습니다. 결과는 높은 fanout입니다. 이 모든 것은 문제가 있는 propagation delay로 요약됩니다.

@empty 및 @full은 제대로 작성된 FIFO에서 flip-flops 의 outputs가 므로 개선할 점이 별로 없습니다. 그러나 FPGA의 소프트웨어는 종종 FIFO를 synthesized netlist로 제공하기 때문에 fanout를 줄이기 위해 이러한 registers를 복제하는 것은 불가능하거나 최소한 어렵습니다. 또한 이러한 registers 와 출력 값을 사용하는 application logic 사이에 FPGA의 logic fabric 에 물리적 거리가 클 수 있습니다. 대형 FPGAs에서 이것은 paths의 delay에 결정적인 기여를 할 수 있습니다.

이 문제에 대한 수정 사항은 @almost_empty 및 @almost_full에 대해 논의할 때 이미 이 페이지 에 제공되었습니다. 이 포트를 사용하여 @wr_en 및 @rd_en 의 outputs는 registers가 될 수 있습니다. 이것은 combinatorial function의 문제를 해결하고 이러한 신호의 fanout를 제어할 수도 있습니다. 게다가 이는 도구가 값을 소비하는 logic 에 더 가깝게 이러한 registers를 배치하는 데 도움이 되므로 propagation delay을 줄이는 데 도움이 됩니다.

@wr_en 및/또는 @din이 critical path에 있는 경우

이 상황은 확실히 해결하기 가장 쉽습니다. registers레이어를 추가하기만 하면 됩니다. 같은 것

always @(posedge wr_clk)
  begin
    wr_en_reg <= wr_en;
    din_reg <= din_reg;
  end

그런 다음 @wr_en_reg 및 @din_reg을 대신 FIFO 에 연결하십시오. overflow에서 FIFO를 방지하려면 @full대신 @almost_full을 사용해야 합니다. 또는 더 일반적으로 말하면 FIFO를 채우기 위한 임계값에서 1을 빼야 합니다.

@rd_en 및/또는 @dout가 critical path에 있는 경우

이제 우리는 심각해지고 있습니다. 이것은 비교적 해결하기 어려운 문제일 뿐만 아니라 발생할 가능성이 가장 높습니다. 그 이유는 다음과 같습니다.

@dout의 경우:

그래서 목표는 @rd_en 와 FIFO의 logic사이에서 combinatorial path를 없애고, @dout에서도 똑같이 하는 것입니다.

@dout의 combinatorial path만 분리

이것을 해결책으로 제시하려는 것은 아니지만 토론은 이것을 다음 단계를 파악하기 위한 준비로 이해하는 데 도움이 될 수 있습니다. 이것이 혼란스럽다면 이 섹션을 건너뛰십시오.

따라서 @dout의 combinatorial path만 분리하려고 한다고 가정합니다. FWFT FIFO를 "standard FIFO"(위 그림 참조)로 변환하는 wrapper module은 정확히 다음을 수행합니다. register를 추가하여 @dout의 combinatorial path를 종료합니다. 그러나 시작점으로 FWFT FIFO가 필요합니다.

그러나 "standard FIFO"를 FWFT FIFO로 변환하는 wrapper module이 있었습니다. 그렇다면 FIFO를 앞뒤로 변환할 수 있습니까? 아니면 동등한 기능을 하는 단일 module을 작성하시겠습니까? 어느 쪽이든, 이 형식의 솔루션은 @rd_en의 상황을 악화시킵니다.

그러나 이 솔루션은 다음을 자세히 살펴볼 가치가 있습니다. FWFT FIFO 로의 변환은 단지 wrapped FIFO의 @dout가 유효할 때 추적을 유지하고 @dout가 유효하지 않을 때(및/또는 외부 @rd_en이 하이일 때) @fifo_rd_en을 하이로 유지하는 것으로 구성되었습니다.

"standard FIFO"로의 변환은 @rd_en이 높을 때 wrapped FIFO의 @dout 값을 register 로 복사하여 수행되었습니다.

따라서 대체로 첫 번째 메커니즘은 가능한 경우 wrapped FIFO의 @dout를 유효한 상태로 유지했으며 두 번째 메커니즘은 외부 @rd_en이 요청했을 때 @dout를 다른 register 로 복사했습니다.

그러나 이것은 @rd_en의 combinatorial path문제를 해결하지 못합니다. 연속 읽기를 허용하려면 외부 @rd_en이 높은 각 clock 의 원래 FIFO 에서 단어를 읽어야 합니다. 그렇지 않으면 FWFT의 @dout가 사용되었지만 업데이트되지 않았기 때문에 무효화됩니다. 따라서 이 내부 FIFO의 @rd_en은 외부 @rd_en의 combinatorial function 여야 합니다. 이것을 변경하려면 다음과 같이 @dout의 path에 다른 register를 추가해야 합니다.

reg_fifo로 두 combinatorial paths 분리

더 이상 고민하지 않고 @rd_en 및 @dout용 combinatorial paths를 분리하는 reg_fifo module입니다.

module reg_fifo(rst,
                rd_clk, rd_en, dout, empty,
                wr_clk, wr_en, din, full);

   parameter width = 8;

   input                 rst;
   input                 rd_clk;
   input                 rd_en;
   input                 wr_clk;
   input                 wr_en;
   input [(width-1):0]   din;
   output                empty;
   output                full;
   output [(width-1):0]  dout;

   reg                   fifo_valid, middle_valid;
   reg [(width-1):0]     dout, middle_dout;

   wire [(width-1):0]    fifo_dout;
   wire                  fifo_empty, fifo_rd_en;
   wire                  will_update_middle, will_update_dout;

   // orig_fifo is "standard" (non-FWFT) FIFO
   fifo orig_fifo
      (
       .rst(rst),
       .rd_clk(rd_clk),
       .rd_en(fifo_rd_en),
       .dout(fifo_dout),
       .empty(fifo_empty),
       .wr_clk(wr_clk),
       .wr_en(wr_en),
       .din(din),
       .full(full)
       );

   assign will_update_middle = fifo_valid && (middle_valid == will_update_dout);
   assign will_update_dout = rd_en && !empty;
   assign fifo_rd_en = !fifo_empty && !(middle_valid && fifo_valid);
   assign empty = !(fifo_valid || middle_valid);

   always @(posedge rd_clk)
      if (rst)
         begin
            fifo_valid <= 0;
            middle_valid <= 0;
            dout <= 0;
            middle_dout <= 0;
         end
      else
         begin
            if (will_update_middle)
               middle_dout <= fifo_dout;

            if (will_update_dout)
               dout <= middle_valid ? middle_dout : fifo_dout;

            if (fifo_rd_en)
               fifo_valid <= 1;
            else if (will_update_middle || will_update_dout)
               fifo_valid <= 0;

            if (will_update_middle)
               middle_valid <= 1;
            else if (will_update_dout)
               middle_valid <= 0;
         end
endmodule

가장 먼저 주목해야 할 점은 @dout가 이 module에 정의된 register가 고 @rd_en이 이 register의 업데이트를 유발한다는 것입니다. 이 두 가지를 @fifo_dout 및 @fifo_rd_en인 내부 FIFO에 연결된 유사한 신호와 혼동하지 않는 것이 중요합니다.

이제 이 module이 어떻게 작동하는지 알아보겠습니다.

pipeline이해하기

FWFT FIFO로의 변환기와 마찬가지로 일반 FIFO, orig_fifo의 instantiation이 있습니다. reg_fifo module 의 logic은 @fifo_dout 에 유효한 값이 없을 때 orig_fifo 에서 단어를 읽어 @fifo_dout의 값을 유효하게 유지하려고 시도합니다. 그러나 그 외에도 @middle_dout라는 두 번째 register가 있습니다. logic은 가능한 경우 @fifo_dout값을 사용하여 이 register 도 유효하게 유지하려고 시도합니다.

따라서 @fifo_dout, @middle_dout 및 @dout를 orig_fifo 의 데이터를 앞으로 이동시키는 pipeline 로 볼 수 있습니다.

이러한 pipeline stages가 유효한 시기를 추적하는 두 개의 registers가 있습니다. @fifo_valid는 @fifo_dout가 유효할 때 하이이고 @middle_valid는 @middle_dout가 유효할 때 하이입니다.

이 pipeline 의 목적은 중간 단계를 우회하는 기능입니다. @rd_en이 높고 @empty가 낮을 때 @dout는 @middle_dout 또는 @fifo_dout에서 새 값을 취하지만 항상 @middle_dout를 선호합니다. 즉, @middle_dout가 유효하면 @dout는 @middle_dout를 사용하고, 그렇지 않으면 @fifo_dout를 대신 사용합니다. 이것이 @rd_en의 combinatorial path를 분리하는 핵심 방법은 나중에 설명합니다.

그럼 먼저 구현의 세부 사항을 살펴 보겠습니다. FIFO의 data output 에서 fifo_reg의 output register로 가는 두 개의 별도 경로가 있습니다. 그들은 이 그림에서 왼쪽과 오른쪽에 별도로 표시됩니다.

Data flow with extra registers

두 pipeline stages (@fifo_dout 및 @middle_dout) 중 어느 것도 유효하지 않은 경우 @empty는 높음으로 데이터를 가져올 곳이 없음을 나타냅니다.

assign empty = !(fifo_valid || middle_valid);

이러한 pipeline stages를 유효하게 유지하려는 시도는

assign fifo_rd_en = !fifo_empty && !(middle_valid && fifo_valid);

즉, 두 pipeline stages 중 하나라도 유효하지 않으면 가능하면 orig_fifo에서 읽습니다. @fifo_dout가 이미 유효한 경우 @fifo_dout가 업데이트되는 동시에 해당 값이 @middle_dout 에 복사됩니다(자세한 내용은 아래 참조).

이제 @will_update_* 쌍의 정의를 살펴보겠습니다.

assign will_update_middle = fifo_valid && (middle_valid == will_update_dout);
assign will_update_dout = rd_en && !empty;

먼저 @will_update_dout는 @rd_en 에 @empty가 높을 때 reg_fifo 에서 읽지 못하도록 하는 안전 장치와 같다는 점에 주의하십시오.

다음으로 @middle_out의 업데이트를 제어하는 @will_update_middle이 있습니다.

always @(posedge rd_clk)
  if (will_update_middle)
     middle_dout <= fifo_dout;

위의 @will_update_middle정의를 보면 @middle_dout를 업데이트하기 위한 두 가지 조건이 있습니다. 하나는 @fifo_dout 의 값이 유효하다는 것인데, 이는 매우 명백하며, (middle_valid == will_update_dout)라는 표현이 있습니다. 전체 기계가 어떻게 작동하는지 설명하므로 이 표현을 4가지 가능한 옵션으로 분류해 보겠습니다. 이 모든 것은 @fifo_dout가 유효한 경우에만 역할을 한다는 점을 명심하십시오.

@middle_valid 와 @fifo_valid가 동시에 하이일 때 @fifo_rd_en은 로우입니다. 결과적으로 마지막 두 경우의 시나리오가 발생할 때 orig_fifo 에서 데이터를 가져오지 않습니다.

특히 두 pipeline stages가 모두 유효하고 @rd_en이 하이일 때 @fifo_dout의 값이 @middle_dout에 복사된다. 그리고 @fifo_rd_en이 로우이기 때문에 @fifo_valid는 다음 clock cycle에서 로우로 변경됩니다. @middle_valid는 높은 상태를 유지하므로 필요한 경우 clock cycle 다음에 데이터를 제공할 수 있습니다. clock cycle 에서 그 후 @fifo_valid는 다시 높아집니다( orig_fifo에 데이터가 있는 경우).

그렇다면 왜 @fifo_rd_en이 이 특정 상황에서 @fifo_dout를 유효하게 유지하도록 정의되지 않습니까? @fifo_rd_en은 @rd_en의 combinatorial function 여야 하기 때문에 이 dual-stage pipeline이 피하도록 설계된 것과 정확히 같습니다.

이제 @dout가 어떻게 정의되는지 살펴볼 차례입니다. reset을 제외하고 정의는 다음과 같습니다.

always @(posedge rd_clk)
  if (will_update_dout)
    dout <= middle_valid ? middle_dout : fifo_dout;

@will_update_dout를 정의로 대체하면 다음과 같습니다.

always @(posedge rd_clk)
  if (rd_en && !empty)
    dout <= middle_valid ? middle_dout : fifo_dout;

이것은 FWFT FIFO 에서 "standard FIFO"로의 변환과 유사하지만 선택할 수 있는 소스는 두 가지뿐입니다. @middle_dout 에 유효한 값이 포함되어 있으면 사용됩니다. 그렇지 않으면 @fifo_dout. 둘 다 유효하지 않으면 @empty가 높으므로 아무 일도 일어나지 않습니다.

이것이 도움이 되는 이유는 무엇입니까? output timing의 경우 @dout는 분명히 register입니다. @rd_en와 관련하여 @fifo_rd_en은 registers인 @middle_valid 와 @fifo_valid에만 의존합니다. 플러스 @fifo_empty는 orig_fifo 자체의 output 입니다(이 combinatorial path는 불가피합니다). 따라서 @fifo_rd_en은 외부 @rd_en에 의존하지 않으므로 @rd_en 에서 orig_fifo까지 combinatorial path가 없습니다.

pipeline stages의 registers유효성 추적

그림을 완성하려면: 두 개의 *_valid 플래그는 관련 register 에 유효한 데이터가 포함되어 있는지 여부를 알려줍니다. @fifo_valid관련:

if (fifo_rd_en)
   fifo_valid <= 1;
 else if (will_update_middle || will_update_dout)
   fifo_valid <= 0;

이것은 위의 "standard FIFO"에서 FWFT FIFO 로의 변환에서 @dout_valid 의 정의와 같습니다. @fifo_rd_en이 rising edge에서 하이일 때, 그 결과 @fifo_valid가 하이가 됩니다. orig_fifo에서 데이터를 읽으면 이 FIFO의 output이 결과적으로 유효한 것으로 간주되기 때문에 이는 의미가 있습니다. 그러나 @fifo_rd_en이 낮고 데이터가 @middle_dout 또는 @dout중 하나로 복사된 경우 @fifo_dout가 더 이상 유효하지 않다고 간주합니다. FIFO의 output은 사용한 지 얼마 되지 않았고, FIFO는 이를 새로운 데이터로 대체하지 않았습니다.

@middle_valid는 동일한 논리를 따릅니다.

if (will_update_middle)
   middle_valid <= 1;
 else if (will_update_dout)
   middle_valid <= 0;

@will_update_middle이 high이면 데이터가 @middle_dout에 복사되므로 @middle_valid 도 high가 됩니다. 그렇지 않고 @middle_dout 의 데이터가 @dout로 복사되면 @middle_valid는 low로 변경됩니다. @dout는 가능하면 @middle_dout에서 복사하는 것을 선호하기 때문에 @will_update_dout는 이에 대한 충분 조건입니다.

그것은 전혀 작동합니까?

이 module은 너무 복잡하여 작동한다는 거의 공식적인 증거가 필요합니다. 따라서 이 질문에 답하는 한 가지 방법은 두 개의 pipelines stages, @fifo_dout 및 @middle_dout중 몇 개가 유효한지 묻는 것입니다. 이 값 은 reg_fifo module에 정의되어 있지 않지만 다음 과 같이 정의 될 수 있습니다.

wire [1:0] valid_count;
assign valid_count = fifo_valid + middle_valid;

이 가상의 @valid_count는 분명히 0, 1 또는 2값을 가질 수 있습니다. 다음과 같이 증가 또는 감소합니다.

logic equations를 살펴보고 이 세 가지 진술이 옳다는 것을 스스로 확신하십시오.

orig_fifo 에 데이터가 있고 application logic이 계속 읽기를 원할 때 어떤 일이 일어나는지 봅시다.

reg_fifo의 logic은 orig_fifo에서 읽어서 @valid_count를 2로 밀어 올리려고 합니다. 반면, @empty는 @valid_count가 0이 아닐 때 로우이므로 @valid_count가 1이 되자마자 @rd_en이 하이가 될 수 있습니다. 따라서 @valid_count가 1일 때 @valid_count가 2가 아니기 때문에 @fifo_rd_en은 하이가 될 것입니다.

그러나 @valid_count는 2의 값에 도달하지 않을 것입니다. 왜냐하면 @rd_en은 높은 값을 유지함으로써 그것을 방지하기 때문입니다. 따라서 데이터는 @fifo_rd_en 와 @rd_en이 모두 high로 유지되고 @valid_count가 1에 남아 있는 상태로 데이터가 흐릅니다. 시작 부분을 제외하고 데이터는 @fifo_dout 에서 @dout로 복사됩니다.

이 손익분기점은 orig_fifo가 비어 있을 때 깨집니다. 이 경우 @fifo_rd_en은 더 이상 하이가 허용되지 않기 때문에 @valid_count는 0이 됩니다. 또 다른 타이 브레이커는 FIFO가 비어 있지 않고 application logic이 더 읽기를 원하지 않기 때문에 @rd_en이 낮아지는 경우입니다. 이 경우 @valid_count는 2로 상승하고 그대로 유지됩니다.

그러나 나중에 @rd_en이 다시 하이가 되면 @valid_count는 1로 내려가고 그 시점에서만 @fifo_rd_en이 하이로 변경됩니다( orig_fifo가 비어 있지 않는 한).

다시 한 번, @valid_count는 module에서 구현되지 않은 이론적인 신호일 뿐입니다. 이 설명은 두 개의 추가 pipeline stages가 데이터의 지속적인 흐름을 보장하는 이유를 이해하는 데 도움이 되었기를 바랍니다.

사용 참고 사항

이 module은 포장된 "standard FIFO"의 드롭인 교체품으로 사용할 수 있습니다. 기능적 관점에서 보면 아무것도 바뀌지 않습니다. 그러나 orig_fifo는 @rd_en, @dout 및 @empty의 동작에 약간의 변화가 있지만 orig_fifo가 FIFO (안전한 가정)처럼 올바르게 동작하는 한 문제가 되지 않습니다. 데이터 쓰기와 관련된 ports는 그대로 전달되기 때문에 아무런 변화가 없습니다.

module은 두 개의 pipelines stages를 추가하기 때문에 orig_fifo의 fill counters는 reg_fifo 에 저장된 총 단어 수보다 낮은 값을 나타낼 수 있습니다(즉, orig_fifo 와 pipeline stages 에 저장된 단어 수를 함께 계산). 따라서 @almost_empty 또는 이와 유사한 ports가 orig_fifo에서 활성화되면 비관적인 그림이 나타날 수 있습니다.

reg_fifo 의 약간의 단점은 @empty output이 register가 아니라 registers2개로 구성된 combinatorial function 라는 것입니다. 이것은 timing에 최적은 아니지만 대부분의 사용 사례에서 최소한의 영향을 미칩니다. 이것은 이 페이지 에 표시된 것처럼 @next_words_in_ram와 같은 정신으로 @next_fifo_valid 및 @next_middle_valid 와 같은 combinatorial registers를 정의하여 수정할 수 있습니다. 여기서는 reg_fifo가 충분히 복잡하기 때문에 여기에서 구현되지 않습니다.

timing이 개선된 FWFT FIFO

이 주제를 끝내기 위해 아래는 reg_fifo와 동일한 작업을 수행하지만 대신 application logic 에 FWFT FIFO를 제공하는 module 입니다. FWFT FIFO가 아닌 standard FIFO를 기반으로 합니다. 그러니 혼동하지 마세요...

reg_fifo 에서 이 module 로의 전환은 내가 이미 FWFT FIFOs에 대해 논의한 것과 거의 동일합니다.

module fwft_reg_fifo(rst,
                     rd_clk, rd_en, dout, empty,
                     wr_clk, wr_en, din, full);

   parameter width = 8;

   input                 rst;
   input                 rd_clk;
   input                 rd_en;
   input                 wr_clk;
   input                 wr_en;
   input [(width-1):0]   din;
   output                empty;
   output                full;
   output [(width-1):0]  dout;

   reg                   middle_valid, dout_valid;
   reg [(width-1):0]     dout, middle_dout;

   wire [(width-1):0]    fifo_dout;
   wire                  fifo_empty, fifo_rd_en;
   wire                  will_update_middle, will_update_dout;

   // orig_fifo is "standard" (non-FWFT) FIFO
   fifo orig_fifo
      (
       .rst(rst),
       .rd_clk(rd_clk),
       .rd_en(fifo_rd_en),
       .dout(fifo_dout),
       .empty(fifo_empty),
       .wr_clk(wr_clk),
       .wr_en(wr_en),
       .din(din),
       .full(full)
       );

   assign will_update_middle = !fifo_empty && (middle_valid == will_update_dout);
   assign will_update_dout = (middle_valid || !fifo_empty) && (rd_en || !dout_valid);
   assign fifo_rd_en = !fifo_empty && !(middle_valid && dout_valid);
   assign empty = !dout_valid;

   always @(posedge rd_clk)
      if (rst)
         begin
            middle_valid <= 0;
            dout_valid <= 0;
            dout <= 0;
            middle_dout <= 0;
         end
      else
         begin
            if (will_update_middle)
               middle_dout <= fifo_dout;

            if (will_update_dout)
               dout <= middle_valid ? middle_dout : fifo_dout;

            if (will_update_middle)
               middle_valid <= 1;
            else if (will_update_dout)
               middle_valid <= 0;

            if (will_update_dout)
               dout_valid <= 1;
            else if (rd_en)
               dout_valid <= 0;
         end
endmodule

이것으로 FIFOs에 대한 시리즈 를 마칩니다.

이 페이지는 영어에서 자동으로 번역됩니다. 불분명한 사항이 있으면 원본 페이지를 참조하십시오.
Copyright © 2021-2024. All rights reserved. (6f913017)