<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      異步FIFO

      一、原理介紹

      這里我就不多寫原理了(Doreen大佬寫的很好),主要記錄一下自己學習異步FIFO過程中的理解。

      1.1 ram讀寫

      看“聲明”中的帖子時,發現同步FIFO和異步FIFO在讀寫ram時有一個明顯的區別。即同步FIFO讀寫ram需要外部輸入的讀寫信號以及空滿信號聯合判斷能否讀寫,而異步FIFO是通過外部輸入的讀寫信號以及空滿信號聯合對讀寫指針的自增進行限制,從而達到控制是否讀寫的目的。

      由于空滿標志的判斷需要將讀寫指針同步到對方的時鐘域下,要打兩拍,所以說空滿標志的判斷是不及時的。例如FIFO深度1024,寫入1024個數據后,開始讀出數據。當讀出一個數據后,就應該可以繼續寫入了,但由于讀地址有兩拍的滯后性,此時出現“假滿”,還不能寫數據,不過這種情況不是不可接受的,只是FIFO的效率降低一點罷了。“假空”也是類似的。

      二、異步FIFO代碼

      2.1 代碼

      `timescale 1ns/1ps
      module AsyncFIFO #(
          parameter DATA_WIDTH = 8,
          parameter FIFO_DEPTH = 16
      )(
          input  wire                     wr_clk_i    ,
          input  wire                     rd_clk_i    ,
          input  wire                     wr_rstn_i   ,
          input  wire                     rd_rstn_i   ,
          
          input  wire                     rd_en_i     ,
          input  wire                     wr_en_i     ,
          input  wire [DATA_WIDTH-1:0]    wr_data_i   ,
      
          output wire [DATA_WIDTH-1:0]    rd_data_o   ,
          output wire                     fifo_full_o ,
          output wire                     fifo_empty_o
      );
      localparam DEPTH = $clog2(FIFO_DEPTH);
      
      reg  [DEPTH:0] wr_ptr_d1;
      reg  [DEPTH:0] rd_ptr_d1;
      reg  [DEPTH:0] wr_ptr_gray_sync1;
      reg  [DEPTH:0] wr_ptr_gray_sync2;
      reg  [DEPTH:0] rd_ptr_gray_sync1;
      reg  [DEPTH:0] rd_ptr_gray_sync2;
      reg            fifo_empty_d1;
      
      wire [DEPTH:0] wr_ptr_gray = (wr_ptr_d1 >> 1) ^ wr_ptr_d1;
      wire [DEPTH:0] rd_ptr_gray = (rd_ptr_d1 >> 1) ^ rd_ptr_d1;
      wire           fifo_empty  = (rd_ptr_gray == wr_ptr_gray_sync2) ? 1'b1 : 1'b0; 
      wire           wr_able     =  wr_en_i && !fifo_full_o;
      wire           rd_able     =  rd_en_i && !fifo_empty;
      wire [DEPTH:0] wr_ptr      = (wr_able) ? wr_ptr_d1 + 1'b1 : wr_ptr_d1;
      wire [DEPTH:0] rd_ptr      = (rd_able) ? rd_ptr_d1 + 1'b1 : rd_ptr_d1;
      
      reg  [DATA_WIDTH-1:0] ram [0:FIFO_DEPTH-1];
      reg  [DATA_WIDTH-1:0] rd_data;
      
      wire [DEPTH-1:0] wr_addr = wr_ptr_d1[DEPTH-1:0]; 
      wire [DEPTH-1:0] rd_addr = rd_ptr_d1[DEPTH-1:0]; 
      
      always @(posedge wr_clk_i)                      if(wr_able)    ram[wr_addr]      <= wr_data_i;
      always @(posedge rd_clk_i or negedge rd_rstn_i) if(!rd_rstn_i) rd_data           <= 'd0; else if(rd_able) rd_data <= ram[rd_addr];
      
      always @(posedge wr_clk_i or negedge wr_rstn_i) if(!wr_rstn_i) wr_ptr_d1         <= 'd0; else wr_ptr_d1           <= wr_ptr;
      always @(posedge rd_clk_i or negedge rd_rstn_i) if(!rd_rstn_i) rd_ptr_d1         <= 'd0; else rd_ptr_d1           <= rd_ptr;
      always @(posedge wr_clk_i or negedge wr_rstn_i) if(!wr_rstn_i) rd_ptr_gray_sync1 <= 'd0; else rd_ptr_gray_sync1   <= rd_ptr_gray;
      always @(posedge rd_clk_i or negedge rd_rstn_i) if(!rd_rstn_i) wr_ptr_gray_sync1 <= 'd0; else wr_ptr_gray_sync1   <= wr_ptr_gray;
      always @(posedge rd_clk_i or negedge rd_rstn_i) if(!rd_rstn_i) fifo_empty_d1     <= 'd0; else fifo_empty_d1       <= fifo_empty;
      
      always @(posedge wr_clk_i or negedge wr_rstn_i) if(!wr_rstn_i) rd_ptr_gray_sync2 <= 'd0; else rd_ptr_gray_sync2   <= rd_ptr_gray_sync1;
      always @(posedge rd_clk_i or negedge rd_rstn_i) if(!rd_rstn_i) wr_ptr_gray_sync2 <= 'd0; else wr_ptr_gray_sync2   <= wr_ptr_gray_sync1;
       
      assign rd_data_o    = rd_data;
      assign fifo_full_o  = (wr_ptr_gray == {~rd_ptr_gray_sync2[DEPTH:DEPTH-1], rd_ptr_gray_sync2[DEPTH-2:0]}) ? 1'b1 : 1'b0;
      assign fifo_empty_o = fifo_empty_d1;
      
      endmodule
      

      設計的整體思路都是模仿Doreen的方法。

      最初寫代碼的時候,fifo_empty_o沒有像上面的代碼一樣延一個時鐘周期,仿真時一直拉高rd_en_i時能正常讀出ram的最后一個值,但是在讀出最后一個值的同時fifo_empty_o就拉高了,我是認為這里有點奇怪,但是覺得既然能正常讀出,應該沒有多大的問題。

      但是后來我想到,如果fifo輸出了fifo_empty_o給讀取電路,讀取電路就應該立即拉低rd_en_i,而不是像上面說的恒拉高rd_en_i,此時就會出現漏讀,像這樣,

      image

      左邊的藍色框正常寫入8個數據,wr_en_i有1ps的延時,所以最左側的上升沿沒有跟寫時鐘上升沿對齊。紅框中是讀出數據,rd_en_i同樣有1ps的延時,只讀出了前7個數據,所以fifo_empty_o應該要延一個時鐘周期

      延一個時鐘周期的圖如下,

      image

      可見紅框中正常讀出了8個數,只是數字8沒有截出來。

      然后考慮到萬一讀電路保持rd_en_i恒為高,能不能正常工作,

      image

      這里rd_en_i就是恒拉高,在讀出8的下一個時鐘上升沿,fifo_empty_o才拉高,我認為這樣才是正確的,而且rd_en_i恒拉高沒有產生任何影響。

      下面是從還沒有寫入數據就開始讀,

      image

      在還沒有寫入數據的時候是讀不出數據的,并且由于指針同步造成的假空,導致fifo_empty_o頻繁翻轉,但是讀數據是正常讀出了8個數。

      上面是慢到快,經過驗證,快到慢、非整數倍時鐘都一樣能正常工作。

      針對fifo_empty_o是否需要延遲這一個時鐘周期,或者本來就應該是這樣(我最初的寫法有問題),歡迎指正。

      2.2 仿真代碼

      `timescale 1ns/1ps
      module AsyncFIFO_tb();
      localparam              DATA_WIDTH = 8;
      localparam              FIFO_DEPTH = 8;
      reg                     wr_clk_i;
      reg                     rd_clk_i;
      reg                     wr_rstn_i;
      reg                     rd_rstn_i;
      reg                     rd_en_i;
      reg                     wr_en_i;
      reg  [DATA_WIDTH-1:0]   wr_data_i;
      wire [DATA_WIDTH-1:0]   rd_data_o;
      wire                    fifo_full_o;
      wire                    fifo_empty_o;
      
      AsyncFIFO #(
          .DATA_WIDTH(DATA_WIDTH),
          .FIFO_DEPTH(FIFO_DEPTH)
      )myFIFO(
          .wr_clk_i       (wr_clk_i),
          .rd_clk_i       (rd_clk_i),
          .wr_rstn_i      (wr_rstn_i),
          .rd_rstn_i      (rd_rstn_i),
          .rd_en_i        (rd_en_i),
          .wr_en_i        (wr_en_i),
          .wr_data_i      (wr_data_i),
          .rd_data_o      (rd_data_o),
          .fifo_full_o    (fifo_full_o),
          .fifo_empty_o   (fifo_empty_o)
      );
      initial begin
          $fsdbDumpfile("test.fsdb");
          $fsdbDumpvars(0, AsyncFIFO_tb);
      end
      
      always #20 wr_clk_i = ~wr_clk_i;
      always #10 rd_clk_i = ~rd_clk_i;
      
      initial begin
          wr_clk_i = 0;
          rd_clk_i = 0;
          wr_rstn_i = 1;
          rd_rstn_i = 1;
          wr_en_i = 0;
          rd_en_i = 0;
          wr_data_i = 0;
          #10;
          wr_rstn_i = 0;
          rd_rstn_i = 0;
          #10;
          wr_rstn_i = 1;
          rd_rstn_i = 1;
      
          repeat(10) @(posedge wr_clk_i);
      
          @(posedge rd_clk_i) #1 rd_en_i = 1;
          repeat(10) @(posedge wr_clk_i);
          @(posedge wr_clk_i) #1 wr_en_i = 1; wr_data_i = 1;
          @(posedge wr_clk_i) #1 wr_data_i = 2;
          @(posedge wr_clk_i) #1 wr_data_i = 3;
          @(posedge wr_clk_i) #1 wr_data_i = 4;
          @(posedge wr_clk_i) #1 wr_data_i = 5;
          @(posedge wr_clk_i) #1 wr_data_i = 6;
          @(posedge wr_clk_i) #1 wr_data_i = 7;
          @(posedge wr_clk_i) #1 wr_data_i = 8;
          @(posedge wr_clk_i) #1 wr_en_i = 0;
      
          //@(posedge rd_clk_i) #1 rd_en_i = 1;
          //repeat(9) @(posedge rd_clk_i);
          //rd_en_i = 0;
          //repeat(10) @(posedge rd_clk_i);
      
          
      
          repeat(100) @(posedge wr_clk_i);
          $finish;
      end
      
      endmodule
      

      聲明

      本文的第一節和第二節參考自<掰開揉碎講 FIFO(同步FIFO和異步FIFO) - Doreen的FPGA自留地 - 博客園>
      http://www.rzrgm.cn/DoreenLiu/p/17348480.html

      posted @ 2025-11-05 17:45  Holybanban  閱讀(2)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久成人影院精品777| 亚洲一区二区约美女探花| 精品无码人妻一区二区三区| 久久久久久曰本av免费免费| 亚洲一区二区av高清| 中文字幕国产在线精品| 国产综合色在线精品| 波多野结衣久久一区二区| 日韩精品国产二区三区| 人妻丰满熟妇av无码处处不卡| 久久综合给合久久狠狠97色 | 天天弄天天模| 熟女人妻精品一区二区视频| 精品中文人妻中文字幕| 亚洲国产无套无码av电影| 国产精品一二区在线观看| 亚洲日韩一区二区| 乱老年女人伦免费视频| 麻豆一区二区三区精品视频| 极品无码国模国产在线观看| 天天爱天天做天天爽夜夜揉| 激情综合色综合久久丁香| 精品国产成人a在线观看| 国产午夜成人久久无码一区二区| 久久精品国产九一九九九| 伊人精品成人久久综合97| 久久国产自偷自偷免费一区| 一区二区三区一级黄色片| 人人澡超碰碰97碰碰碰| 国产精品亚洲一区二区z| 亚洲精品无码日韩国产不卡av| 极品少妇无套内射视频| 国语自产精品视频在线看| 精品一二三四区在线观看| 巨熟乳波霸若妻在线播放| 日韩精品在线观看一二区| 日本高清在线观看WWW色| 亚洲V天堂V手机在线| 九九热在线视频只有精品| 日日摸夜夜添夜夜添国产三级| 国产男女黄视频在线观看|