異步FIFO設計(Verilog)

FIFO(First In First Out)是異步數據傳輸時經常使用的存儲器。該存儲器的特點是數據先進先出(后進后出)。其實,多位寬數據的異步傳輸問題,無論是從快時鐘到慢時鐘域,還是從慢時鐘到快時鐘域,都可以使用 FIFO 處理。


FIFO 原理

工作流程

復位之后,在寫時鐘和狀態信號的控制下,數據寫入 FIFO 中。RAM 的寫地址從 0 開始,每寫一次數據寫地址指針加一,指向下一個存儲單元。當 FIFO 寫滿后,數據將不能再寫入,否則數據會因覆蓋而丟失。

FIFO 數據為非空、或滿狀態時,在讀時鐘和狀態信號的控制下,可以將數據從 FIFO 中讀出。RAM 的讀地址從 0 開始,每讀一次數據讀地址指針加一,指向下一個存儲單元。當 FIFO 讀空后,就不能再讀數據,否則讀出的數據將是錯誤的。

FIFO 的存儲結構為雙口 RAM,所以允許讀寫同時進行。典型異步 FIFO 結構圖如下所示。端口及內部信號將在代碼編寫時進行說明。

讀寫時刻

關于寫時刻,只要 FIFO 中數據為非滿狀態,就可以進行寫操作;如果 FIFO 為滿狀態,則禁止再寫數據。關于讀時刻,只要 FIFO 中數據為非空狀態,就可以進行讀操作;如果 FIFO 為空狀態,則禁止再讀數據。不管怎樣,一段正常讀寫 FIFO 的時間段,如果讀寫同時進行,則要求寫 FIFO 速率不能大于讀速率。

讀空狀態

開始復位時,FIFO 沒有數據,空狀態信號是有效的。當 FIFO 中被寫入數據后,空狀態信號拉低無效。當讀數據地址追趕上寫地址,即讀寫地址都相等時,FIFO 為空狀態。

因為是異步 FIFO,所以讀寫地址進行比較時,需要同步打拍邏輯,就需要耗費一定的時間。所以空狀態的指示信號不是實時的,會有一定的延時。如果在這段延遲時間內又有新的數據寫入 FIFO,就會出現空狀態指示信號有效,但是 FIFO 中其實存在數據的現象。

嚴格來講該空狀態指示是錯誤的。但是產生空狀態的意義在于防止讀操作對空狀態的 FIFO 進行數據讀取。產生空狀態信號時,實際 FIFO 中有數據,相當于提前判斷了空狀態信號,此時不再進行讀 FIFO 數據操作也是安全的。所以,該設計從應用上來說是沒有問題的。

寫滿狀態

開始復位時,FIFO 沒有數據,滿信號是無效的。當 FIFO 中被寫入數據后,此時讀操作不進行或讀速率相對較慢,只要寫數據地址超過讀數據地址一個 FIFO 深度時,便會產生滿狀態信號。此時寫地址和讀地址也是相等的,但是意義是不一樣的。

此時經常使用多余的 1bit 分別當做讀寫地址的拓展位,來區分讀寫地址相同的時候,FIFO 的狀態是空還是滿狀態。當讀寫地址與拓展位均相同的時候,表明讀寫數據的數量是一致的,則此時 FIFO 是空狀態。如果讀寫地址相同,拓展位為相反數,表明寫數據的數量已經超過讀數據數量的一個 FIFO 深度了,此時 FIFO 是滿狀態。當然,此條件成立的前提是空狀態禁止讀操作、滿狀態禁止寫操作。

同理,由于異步延遲邏輯的存在,滿狀態信號也不是實時的。但是也相當于提前判斷了滿狀態信號,此時不再進行寫 FIFO 操作也不會影響應用的正確性。


FIFO 設計

設計要求

為設計應用于各種場景的 FIFO,這里對設計提出如下要求:

  • (1) FIFO 深度、寬度參數化,輸出空、滿狀態信號,并輸出一個可配置的滿狀態信號。當 FIFO 內部數據達到設置的參數數量時,拉高該信號。
  • (2) 輸入數據和輸出數據位寬可以不一致,但要保證寫數據、寫地址位寬與讀數據、讀地址位寬的一致性。例如寫數據位寬 8bit,寫地址位寬為 6bit(64 個數據)。如果輸出數據位寬要求 32bit,則輸出地址位寬應該為 4bit(16 個數據)。
  • (3) FIFO 是異步的,即讀寫控制信號來自不同的時鐘域。輸出空、滿狀態信號之前,讀寫地址信號要用格雷碼做同步處理,通過減少多位寬信號的翻轉來減少打拍法同步時數據的傳輸錯誤。 格雷碼與二進制之間的轉換如下圖所示。

雙口 RAM 設計

RAM 端口參數可配置,讀寫位寬可以不一致。建議 memory 數組定義時,以長位寬地址、短位寬數據的參數為參考,方便數組變量進行選擇訪問。

Verilog 描述如下。

module  ramdp#(  parameter       AWI     = 5 ,parameter       AWO     = 7 ,parameter       DWI     = 64 ,parameter       DWO     = 16)(input                   CLK_WR , //寫時鐘input                   WR_EN ,  //寫使能input [AWI-1:0]         ADDR_WR ,//寫地址input [DWI-1:0]         D ,      //寫數據input                   CLK_RD , //讀時鐘input                   RD_EN ,  //讀使能input [AWO-1:0]         ADDR_RD ,//讀地址output reg [DWO-1:0]    Q        //讀數據);//輸出位寬大于輸入位寬,求取擴大的倍數及對應的位數parameter       EXTENT       = DWO/DWI ;parameter       EXTENT_BIT   = AWI-AWO > 0 ? AWI-AWO : 'b1 ;//輸入位寬大于輸出位寬,求取縮小的倍數及對應的位數parameter       SHRINK       = DWI/DWO ;parameter       SHRINK_BIT   = AWO-AWI > 0 ? AWO-AWI : 'b1;genvar i ;generate//數據位寬展寬(地址位寬縮小)if (DWO >= DWI) begin//寫邏輯,每時鐘寫一次reg [DWI-1:0]         mem [(1<<AWI)-1 : 0] ;always @(posedge CLK_WR) beginif (WR_EN) beginmem[ADDR_WR]  <= D ;endend//讀邏輯,每時鐘讀 4 次for (i=0; i<EXTENT; i=i+1) beginalways @(posedge CLK_RD) beginif (RD_EN) beginQ[(i+1)*DWI-1: i*DWI]  <= mem[(ADDR_RD*EXTENT) + i ] ;endendendend//=================================================//數據位寬縮小(地址位寬展寬)else begin//寫邏輯,每時鐘寫 4 次reg [DWO-1:0]         mem [(1<<AWO)-1 : 0] ;for (i=0; i<SHRINK; i=i+1) beginalways @(posedge CLK_WR) beginif (WR_EN) beginmem[(ADDR_WR*SHRINK)+i]  <= D[(i+1)*DWO -1: i*DWO] ;endendend//讀邏輯,每時鐘讀 1 次always @(posedge CLK_RD) beginif (RD_EN) beginQ <= mem[ADDR_RD] ;endendendendgenerateendmodule

計數器設計

計數器用于產生讀寫地址信息,位寬可配置,不需要設置結束值,讓其溢出后自動重新計數即可。Verilg 描述如下。

module  ccnt#(parameter W )(input              rstn ,input              clk ,input              en ,output [W-1:0]     count);reg [W-1:0]          count_r ;always @(posedge clk or negedge rstn) beginif (!rstn) begincount_r        <= 'b0 ;endelse if (en) begincount_r        <= count_r + 1'b1 ;endendassign count = count_r ;endmodule

FIFO 設計

該模塊為 FIFO 的主體部分,產生讀寫控制邏輯,并產生空、滿、可編程滿狀態信號。

鑒于篇幅原因,這里只給出讀數據位寬大于寫數據位寬的邏輯代碼。

module  fifo#(  parameter       AWI        = 5 ,parameter       AWO        = 3 ,parameter       DWI        = 4 ,parameter       DWO        = 16 ,parameter       PROG_DEPTH = 16) //可設置深度(input                   rstn,  //讀寫使用一個復位input                   wclk,  //寫時鐘input                   winc,  //寫使能input [DWI-1: 0]        wdata, //寫數據input                   rclk,  //讀時鐘input                   rinc,  //讀使能output [DWO-1 : 0]      rdata, //讀數據output                  wfull,    //寫滿標志output                  rempty,   //讀空標志output                  prog_full //可編程滿標志);//輸出位寬大于輸入位寬,求取擴大的倍數及對應的位數parameter       EXTENT       = DWO/DWI ;parameter       EXTENT_BIT   = AWI-AWO ;//輸出位寬小于輸入位寬,求取縮小的倍數及對應的位數parameter       SHRINK       = DWI/DWO ;parameter       SHRINK_BIT   = AWO-AWI ;//==================== push/wr counter ===============wire [AWI-1:0]      waddr ;wire                wover_flag ; //多使用一位做寫地址拓展ccnt         #(.W(AWI+1))            u_push_cnt(.rstn           (rstn),.clk            (wclk),.en             (winc && !wfull), //full 時禁止寫.count          ({wover_flag, waddr}));//============== pop/rd counter ===================wire [AWO-1:0]            raddr ;wire                      rover_flag ;  //多使用一位做讀地址拓展ccnt         #(.W(AWO+1))    u_pop_cnt(.rstn           (rstn),.clk            (rclk),.en             (rinc & !rempty), //empyt 時禁止讀.count          ({rover_flag, raddr}));//==============================================//窄數據進,寬數據出
generateif (DWO >= DWI) begin : EXTENT_WIDTH//格雷碼轉換wire [AWI:0] wptr    = ({wover_flag, waddr}>>1) ^ ({wover_flag, waddr}) ;//將寫數據指針同步到讀時鐘域reg [AWI:0]  rq2_wptr_r0 ;reg [AWI:0]  rq2_wptr_r1 ;always @(posedge rclk or negedge rstn) beginif (!rstn) beginrq2_wptr_r0     <= 'b0 ;rq2_wptr_r1     <= 'b0 ;endelse beginrq2_wptr_r0     <= wptr ;rq2_wptr_r1     <= rq2_wptr_r0 ;endend//格雷碼轉換wire [AWI-1:0] raddr_ex = raddr << EXTENT_BIT ;wire [AWI:0]   rptr     = ({rover_flag, raddr_ex}>>1) ^ ({rover_flag, raddr_ex}) ;//將讀數據指針同步到寫時鐘域reg [AWI:0]    wq2_rptr_r0 ;reg [AWI:0]    wq2_rptr_r1 ;always @(posedge wclk or negedge rstn) beginif (!rstn) beginwq2_rptr_r0     <= 'b0 ;wq2_rptr_r1     <= 'b0 ;endelse beginwq2_rptr_r0     <= rptr ;wq2_rptr_r1     <= wq2_rptr_r0 ;endend//格雷碼反解碼//如果只需要空、滿狀態信號,則不需要反解碼//因為可編程滿狀態信號的存在,地址反解碼后便于比較reg [AWI:0]       wq2_rptr_decode ;reg [AWI:0]       rq2_wptr_decode ;integer           i ;always @(*) beginwq2_rptr_decode[AWI] = wq2_rptr_r1[AWI];for (i=AWI-1; i>=0; i=i-1) beginwq2_rptr_decode[i] = wq2_rptr_decode[i+1] ^ wq2_rptr_r1[i] ;endendalways @(*) beginrq2_wptr_decode[AWI] = rq2_wptr_r1[AWI];for (i=AWI-1; i>=0; i=i-1) beginrq2_wptr_decode[i] = rq2_wptr_decode[i+1] ^ rq2_wptr_r1[i] ;endend//讀寫地址、拓展位完全相同是,為空狀態assign rempty    = (rover_flag == rq2_wptr_decode[AWI]) &&(raddr_ex >= rq2_wptr_decode[AWI-1:0]);//讀寫地址相同、拓展位不同,為滿狀態assign wfull     = (wover_flag != wq2_rptr_decode[AWI]) &&(waddr >= wq2_rptr_decode[AWI-1:0]) ;//拓展位一樣時,寫地址必然不小于讀地址//拓展位不同時,寫地址部分比如小于讀地址,實際寫地址要增加一個FIFO深度assign prog_full  = (wover_flag == wq2_rptr_decode[AWI]) ?waddr - wq2_rptr_decode[AWI-1:0] >= PROG_DEPTH-1 :waddr + (1<<AWI) - wq2_rptr_decode[AWI-1:0] >= PROG_DEPTH-1;//雙口 ram 例化ramdp#( .AWI     (AWI),.AWO     (AWO),.DWI     (DWI),.DWO     (DWO))u_ramdp(.CLK_WR          (wclk),.WR_EN           (winc & !wfull), //寫滿時禁止寫.ADDR_WR         (waddr),.D               (wdata[DWI-1:0]),.CLK_RD          (rclk),.RD_EN           (rinc & !rempty), //讀空時禁止讀.ADDR_RD         (raddr),.Q               (rdata[DWO-1:0]));end//==============================================//big in and small out/*else begin: SHRINK_WIDTH……end*/endgenerate
endmodule

FIFO 調用

下面可以調用設計的 FIFO,完成多位寬數據傳輸的異步處理。

寫數據位寬為 4bit,寫深度為 32。

讀數據位寬為 16bit,讀深度為 8,可配置 full 深度為 16。

module  fifo_s2b(input                   rstn,input [4-1: 0]          din,     //異步寫數據input                   din_clk, //異步寫時鐘input                   din_en,  //異步寫使能output [16-1 : 0]       dout,      //同步后數據input                   dout_clk,  //同步使用時鐘input                   dout_en ); //同步數據使能wire         fifo_empty, fifo_full, prog_full ;wire         rd_en_wir ;wire [15:0]  dout_wir ;//讀空狀態時禁止讀,否則一直讀assign rd_en_wir     = fifo_empty ? 1'b0 : 1'b1 ;fifo  #(.AWI(5), .AWO(3), .DWI(4), .DWO(16), .PROG_DEPTH(16))u_buf_s2b(.rstn           (rstn),.wclk           (din_clk),.winc           (din_en),.wdata          (din),.rclk           (dout_clk),.rinc           (rd_en_wir),.rdata          (dout_wir),.wfull          (fifo_full),.rempty         (fifo_empty),.prog_full      (prog_full));//緩存同步后的數據和使能reg          dout_en_r ;always @(posedge dout_clk or negedge rstn) beginif (!rstn) begindout_en_r       <= 1'b0 ;endelse begindout_en_r       <= rd_en_wir ;endendassign       dout    = dout_wir ;assign       dout_en = dout_en_r ;endmodule

testbench

`timescale 1ns/1ns
`define         SMALL2BIG
module test ;`ifdef SMALL2BIGreg          rstn ;reg          clk_slow, clk_fast ;reg [3:0]    din ;reg          din_en ;wire [15:0]  dout ;wire         dout_en ;//resetinitial beginclk_slow  = 0 ;clk_fast  = 0 ;rstn      = 0 ;#50 rstn  = 1 ;end//讀時鐘 clock_slow 較快于寫時鐘 clk_fast 的 1/4//保證讀數據稍快于寫數據parameter CYCLE_WR = 40 ;always #(CYCLE_WR/2/4) clk_fast = ~clk_fast ;always #(CYCLE_WR/2-1) clk_slow = ~clk_slow ;//data generateinitial begindin       = 16'h4321 ;din_en    = 0 ;wait (rstn) ;//(1) 測試 full、prog_full、empyt 信號force test.u_data_buf2.u_buf_s2b.rinc = 1'b0 ;repeat(32) begin@(negedge clk_fast) ;din_en = 1'b1 ;din    = {$random()} % 16;end@(negedge clk_fast) din_en = 1'b0 ;//(2) 測試數據讀寫#500 ;rstn = 0 ;#10 rstn = 1 ;release test.u_data_buf2.u_buf_s2b.rinc;repeat(100) begin@(negedge clk_fast) ;din_en = 1'b1 ;din    = {$random()} % 16;end//(3) 停止讀取再一次測試 empyt、full、prog_full 信號force test.u_data_buf2.u_buf_s2b.rinc = 1'b0 ;repeat(18) begin@(negedge clk_fast) ;din_en = 1'b1 ;din    = {$random()} % 16;endendfifo_s2b u_data_buf2(.rstn           (rstn),.din            (din),.din_clk        (clk_fast),.din_en         (din_en),.dout           (dout),.dout_clk       (clk_slow),.dout_en        (dout_en));`else
`endif//stop siminitial beginforever begin#100;if ($time >= 5000)  $finish ;endendendmodule

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253775.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253775.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253775.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python中RabbitMQ的使用(路由鍵模糊匹配)

路由鍵模糊匹配 使用正則表達式進行匹配。其中“#”表示所有、全部的意思&#xff1b;“*”只匹配到一個詞。 匹配規則&#xff1a; 路由鍵&#xff1a;routings [ happy.work, happy.life , happy.work.teacher, sad.work, sad.life, sad.work.teacher ] "#"&am…

數據倉庫事實表分類[轉]

1&#xff09;在數據倉庫領域有一個概念叫Transaction fact table&#xff0c;中文一般翻譯為“事務事實表”。 事務事實表是維度建模的數據倉庫中三種基本類型事實表中的一種&#xff0c;另外兩種分別是周期快照事實表和累積快照事實表。 事務事實表與周期快照事實表、累積快…

嵌入式系統文件系統比較 jffs2, yaffs, cramfs, romfs, ramdisk, ramfs/tmpfs

Linux支持多種文件系統&#xff0c;包括ext2、ext3、vfat、ntfs、iso9660、jffs、romfs和nfs等&#xff0c;為了對各類文件系統 進行統一管理&#xff0c;Linux引入了虛擬文件系統VFS(Virtual File System)&#xff0c;為各類文件系統提供一個統一的操作界面和應用編程接口。 …

Codeforces Beta Round #17 C. Balance DP

C. Balance題目鏈接 http://codeforces.com/contest/17/problem/C 題面 Nick likes strings very much, he likes to rotate them, sort them, rearrange characters within a string... Once he wrote a random string of characters a, b, c on a piece of paper and began t…

時鐘切換處理(Verilog)

隨著各種應用場景的限制&#xff0c;芯片在運行時往往需要在不同的應用下切換不同的時鐘源&#xff0c;例如低功耗和高性能模式就分別需要低頻率和高頻率的時鐘。兩個時鐘源有可能是同源且同步的&#xff0c;也有可能是不相關的。直接使用選擇邏輯進行時鐘切換大概率會導致分頻…

SSH整合中,使用父action重構子類action類.(在父類中獲取子類中的泛型對象)

import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven;/*** 文件名 : BaseAction.java* 提取SSH中的action類* 由于SSH的action中采用模型驅動的方法,使用泛…

用BusyBox制作Linux根文件系統

STEP 1&#xff1a;構建目錄結構 創建根文件系統目錄&#xff0c;主要包括以下目錄 /dev /etc /lib /usr /var /proc /tmp /home /root /mnt /bin /sbin /sys #mkdir /home/rootfs #cd /home/rootfs #mkdir dev etc lib usr var proc tmp home roo…

Angular Elements 組件在非angular 頁面中使用的DEMO

2019獨角獸企業重金招聘Python工程師標準>>> 一、Angular Elements 介紹 Angular Elements 是伴隨Angular6.0一起推出的新技術。它借助Chrome瀏覽器的ShadowDom API&#xff0c;實現一種自定義組件。 這種組件可以用Angular普通組件的開發技術進行編寫&#xff0c;…

(轉) android里,addContentView()動態增加view控件,并實現控件的頂部,中間,底部布局...

http://blog.csdn.net/bfboys/article/details/52563089轉載于:https://www.cnblogs.com/zhangminghan/p/6182909.html

verilog仿真——$test$plusargs 和 $value$plusargs

VERILOG的參數可以用define和parameter的方式定義&#xff0c;這種方法要求我們在編譯前將變量必須定義好&#xff0c;編譯完成之后再也不能修改&#xff1b; 然而&#xff0c;有時候我們在進行仿真時&#xff0c;需要從外部傳遞參數&#xff0c;這個要求怎么滿足呢&#xff1…

盧卡斯定理

盧卡斯定理:解決一類組合數取模問題 A、B是非負整數&#xff0c;p是質數。AB寫成p進制&#xff1a;Aa[n]a[n-1]...a[0]&#xff0c;Bb[n]b[n-1]...b[0]。 則組合數C(A,B)與C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0]) modp同余 即&#xff1a;Lucas(n,m,p)c(n%p,m%p)*Luc…

內核理解

在純技術方面&#xff0c;內核是硬件與軟件之間的一個中間層。其作用是將應用程序的請求傳遞給硬件&#xff0c;并充當底層的驅動程序&#xff0c;對系統中的各種設備和組件。內核啟動init程序作為第一個進程&#xff0c;該進程負責進一步的系統初始化操作&#xff0c;并顯示登…

loadrunner中對https證書的配置

1、準備好網站的證書&#xff0c;一般證書是cer格式&#xff1b; 2、因為loadrunner只支持pem格式的證書&#xff0c;所以要將證書轉換格式&#xff0c;利用openssl工具&#xff1b;&#xff08;或者直接讓開發提供pem格式的證書&#xff09;3、得到pem格式的證書之后&#xff…

Android 9 Pie震撼來襲 同步登陸WeTest

作者&#xff1a;We Test小編商業轉載請聯系騰訊WeTest獲得授權&#xff0c;非商業轉載請注明出處。原文鏈接&#xff1a;wetest.qq.com/lab/view/40…WeTest 導讀2018年8月7日&#xff0c;Google對外發布最新 Android 9.0 正式版系統&#xff0c;并宣布系統版本Android P 被正…

Datapath綜合代碼規范(Verilog)

一、一般準則 1、有符號數運算 利用類型“signed”完成有符號數運算&#xff0c;而不是用無符號數模擬有符號數運算。這樣可以得到更好的QoR。在資源報告中檢查操作數的類型和大小。 2、符號/零擴展 盡量不要手動擴展。verilog利用signed/unsigned會自動完成擴展。這樣代碼可…

Linux下V4L2編程小結

http://www.360doc.com/content/12/0318/16/532901_195392228.shtml :davind dm365linux移植 http://www.embedhq.org/html/jsbw/2010/0425/390.html :Linux下V4L2編程小結

百(垃)度(圾)之星初賽B hdu6114

Chess 題意&#xff1a;中文題 思路&#xff1a;其實就是在n個格子上放m個棋子&#xff08;n>m&#xff09;&#xff08;xjb套Lucas的板子... AC代碼&#xff1a; #include "iostream" #include "string.h" #include "stack" #include "…

variable 'xxx' unsafe in 'case'的處理

問題描述&#xff1a; case get(?Player_LoopTaskInfo) of{TargetCnt, TaskStar, TaskExp} ->ok;_ ->throw("not_found_loop_task_info") end 在case語句中&#xff0c;這樣寫&#xff0c;編譯時&#xff0c;會提示變量unsafe&#xff0c;解決編譯器報錯的…

SDUT 3347 數據結構實驗之數組三:快速轉置

數據結構實驗之數組三&#xff1a;快速轉置 Time Limit: 1000 ms Memory Limit: 65536 KiBProblem Description 轉置運算是一種最簡單的矩陣運算&#xff0c;對于一個m*n的矩陣M( 1 < m < 10000,1 < n < 10000 )&#xff0c;它的轉置矩陣T是一個n*m的矩陣&…

linux設備和驅動加載的先后順序

Linux驅動先注冊總線&#xff0c;總線上可以先掛device&#xff0c;也可以先掛driver&#xff0c;那么究竟怎么控制先后的順序呢。 Linux系統使用兩種方式去加載系統中的模塊&#xff1a;動態和靜態。 靜態加載&#xff1a;將所有模塊的程序編譯到Linux內核中&#xff0c;由do_…