皋陶 发表于 2021-3-6 14:34:57

E203 同步fifo

1. 输入端,      输入信号, i_vld,表示输入请求写同步fifo,如果fifo不满,则fifo发送i_rdy 到输入端,开始写fifo。i_vld和i_rdy是写握手信号。
2.输出端      o_rdy表示接受端已经准备好了,可以读取fifo,o_vld表示fifo准备好了,不为空,可以输出到接收端。o_rdy和o_vld是握手信号。
3.如果fifo是深度为0,则是bypass模式,只要请求写,就直接准备输出,所以
assign o_vld = i_vld;
assign i_rdy = o_rdy;
assign o_dat = i_dat;
可以立即发送数据 o_dat=idat, 对于深度为n的情况, i_rdy = ~full, o_vld=~empty, 就是i_rdy和o_vld是满空标志取反。


4.如果深度为n,假设为4, 我们设置i_vld=1,但是o_rdy 总是0,则输入端写fifo,直到fifo满。
testbench代码如下:module sirv_gnrl_dffs_tb;

   regclk=0,rst_n;
   regi_vld, o_rdy;
   reg i_dat;

   wire i_rdy, o_vld;
   wire o_dat;

   sirv_gnrl_fifo#(.CUT_READY(1),.DP(4),.DW(32)) mybuf(.i_vld(i_vld),.i_rdy(i_rdy),.i_dat(i_dat),.o_vld(o_vld),.o_rdy(o_rdy),.o_dat(o_dat),.clk(clk),.rst_n(rst_n));

   always #10 clk=~clk;

   initial
   begin
      rst_n=1'b1;
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = 32'h12345678;
      #20
      rst_n=1'b0;
      #80
      rst_n=1'b1;
      #80
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #80
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%32;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%1024;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%1024;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%1024;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%1024;
      #20
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = $random()%1024;
      #500 $finish;
   end

   initial
         $monitor($time,,,"clk=%b,rst_n=%b,i_vld=%b,o_rdy=%b, i_rdy=%b, o_vld=%b,",clk,rst_n,i_vld,o_rdy,i_rdy,o_vld);
   initial
   begin
         //$dumpfile("dump.vcd");
         //$dumpvars;
         $fsdbDumpfile("dump.fsdb");
      $fsdbDumpvars("+all");
   end



   endmodule



初始时刻,同步fifo各个信号都是x,直到复位信号rst_n的下降沿到来时候,复位读写指针触发器和向量寄存器触发器,向量寄存器主要用于空满判断。
读写指针都被初始化为1,如果fifo深度(DP)=1,则读写指针rptr_vec_r,wptr_vec_r总是1
下一个读写指针rptr_vec_nxt/wptr_vec_nxt 等于当前指针左移一位,假设DP=4,则指针为0001,0010,0100,1000, 1000的下一个是0001,首尾相接。
这种表示法是用独热码的方式表示读写指针。
////////////////
    ///////// Read-Pointer and Write-Pointer
    // if ith is 1, then means read/write ith value of fifo
    wire rptr_vec_nxt;
    wire rptr_vec_r;
    wire wptr_vec_nxt;
    wire wptr_vec_r;
    //read pointer dynamic process,
    // always is 1
    if(DP == 1) begin:rptr_dp_1
      assign rptr_vec_nxt = 1'b1;
    end
    else begin:rptr_dp_not_1   // 0001,0010,0100,1000,0001, ring buffer,
      assign rptr_vec_nxt =
          rptr_vec_r ? {{DP-1{1'b0}}, 1'b1} :
                        (rptr_vec_r << 1);
    end

    if(DP == 1) begin:wptr_dp_1
      assign wptr_vec_nxt = 1'b1;
    end
    else begin:wptr_dp_not_1
      assign wptr_vec_nxt =
          wptr_vec_r ? {{DP-1{1'b0}}, 1'b1} :
                        (wptr_vec_r << 1);
    end
向量寄存器触发器为DP+1位。复位下降沿道来时候,vec_r=1, 如果读和写有一个工作的时候,则vec_en=1。
如果写enable的时候,vec_nxt = {vec_r,1’b1},
vec_r, vec_nxt: 1,    1111,111111, 11111111, 1_1111i_rdy = (~i_vec);
如果i_vec=1,则表示fifo满了, i_rdy=0,此时不能写fifo了。
///////// Vec register to easy full and empty and the o_vld generation with flop-clean
    wire i_vec;
    wire o_vec;
    wire vec_nxt;
    wire vec_r;

    wire vec_en = (ren ^ wen );//ren not same as wen set to 1
    assign vec_nxt = wen ? {vec_r, 1'b1} : (vec_r >> 1);

    sirv_gnrl_dfflrs #(1)vec_0_dfflrs   (vec_en, vec_nxt   , vec_r   ,   clk, rst_n);
    sirv_gnrl_dfflr#(DP) vec_31_dfflr   (vec_en, vec_nxt, vec_r,   clk, rst_n);

    assign i_vec = {1'b0,vec_r};
    assign o_vec = {1'b0,vec_r};
会根据wen和写指针wptr_vec_r,把i_dat写入到指定的位置,因为是读热码,所以只有一位为1,为1的位置正好是fifo中最上面空的位置。
///////// write fifo
    for (i=0; i<DP; i=i+1) begin:fifo_rf//{
      assign fifo_rf_en = wen & wptr_vec_r;
      // Write the FIFO registers
      sirv_gnrl_dffl#(DW) fifo_rf_dffl (fifo_rf_en, i_dat, fifo_rf_r, clk);
    end//}
对于这个test,会在连续的4个时钟上升沿写入i_dat,因为此时i_dat都为4,所以会写入四个4。
我们修改testbench,增加enable o_rdy
module sirv_gnrl_dffs_tb;

   regclk=0,rst_n;
   regi_vld, o_rdy;
   reg i_dat;

   wire i_rdy, o_vld;
   wire o_dat;

   sirv_gnrl_fifo#(.CUT_READY(1),.DP(4),.DW(32)) mybuf(.i_vld(i_vld),.i_rdy(i_rdy),.i_dat(i_dat),.o_vld(o_vld),.o_rdy(o_rdy),.o_dat(o_dat),.clk(clk),.rst_n(rst_n));

   always #10 clk=~clk;

   initial
   begin
      rst_n=1'b1;
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = 32'h12345678;
      #20
      rst_n=1'b0;
      #80
      rst_n=1'b1;
      #80
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = 32'h8;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = 32'h12;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = 32'h2;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = 32'h11;
      #20
      i_vld = 1'b1;
      o_rdy = 1'b0;
      i_dat = 32'h13;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = 32'h6;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = 32'h22;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b0;
      i_dat = 32'h99;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b1;
      i_dat = 32'h33;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b1;
      i_dat = 32'h17;
      #20
      i_vld = 1'b0;
      o_rdy = 1'b1;
      i_dat = 32'h3;
      #500 $finish;
   end

   initial
         $monitor($time,,,"clk=%b,rst_n=%b,i_vld=%b,o_rdy=%b, i_rdy=%b, o_vld=%b,",clk,rst_n,i_vld,o_rdy,i_rdy,o_vld);
   initial
   begin
         //$dumpfile("dump.vcd");
         //$dumpvars;
         $fsdbDumpfile("dump.fsdb");
      $fsdbDumpvars("+all");
   end



   endmodule

// o_vld as flop-clean
assign o_vld = (o_vec);

根据o_vec判断是否为空,如果不为空,则enable o_vld,开始读fifo。
根据当前的读指针,从fifo中取出数据,并赋值给o_dat, 如果设置了mask out,会把数据初始值中的x mask掉。默认设置这个值。
/////////One-Hot Mux as the read path
    integer j;
    reg mux_rdat;
    always @*
    begin : rd_port_PROC//{
      mux_rdat = {DW{1'b0}};
      for(j=0; j<DP; j=j+1) begin
      mux_rdat = mux_rdat | ({DW{rptr_vec_r}} & fifo_rf_r);
      end
    end//}

    if(MSKO == 1) begin:mask_output//{
      // Mask the data with valid since the FIFO register is not reset and as X
      assign o_dat = {DW{o_vld}} & mux_rdat;
    end//}

else begin:no_mask_output//{
   // Not Mask the data with valid since no care with X for datapth
   assign o_dat = mux_rdat;
end//}
再看下CUT_READY, 当fifo深度为1的时候,ready和下一个阶段的ready信号相关,变成了逻辑链。我们可以通过CUT_READY来控制它。
如果设置了CUT_READY,仅当fifo不为空的时候,才能设置i_rdy信号,可以切断反压信号,阻止它传递到上一级,这时候2个周期传输一个数据。如果设置CUT_READY=0,则变成一个popping操作,同一个周期放入弹出。
if(DP == 1) begin:cut_dp_eq1//{
      if(CUT_READY == 1) begin:cut_ready//{
          // If cut ready, then only accept when fifo is not full
          assign i_rdy = (~i_vec);
      end//}
      else begin:no_cut_ready//{
          // If not cut ready, then can accept when fifo is not full or it is popping
          assign i_rdy = (~i_vec) | ren;
      end//}
    end//}
    else begin : no_cut_dp_gt1//}{
      assign i_rdy = (~i_vec);
    end//}
下面是DP=1,设置CUT_READY=1 和0的波形。
CUT_READY=1



CUT_READY=0


页: [1]
查看完整版本: E203 同步fifo