FIFO缓存区设计
目錄
1.緩存區的制作
2 改善上述緩存區
3 緩存區的實現
1.FIFO (First Input First Output)?一種先進先出的數據緩存器,先進入的數據先從FIFO緩存器中讀出,與RAM相比沒有外部讀寫地址線,使用比較簡單,但只能順序寫入數據,順序的讀出數據,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。
2.FIFO一般用于不同時鐘域之間的數據傳輸,比如FIFO的一端是AD數據采集,另一端為PCI總線,那么在兩個不同的時鐘域間就可以采用FIFO來作為數據緩沖。另外對于不同寬度的數據接口也可以用FIFO,例如單片機位8位數據輸出,而DSP可能是16位數據輸入,在單片機與DSP連接時就可以使用FIFO來達到數據匹配的目的。
3.可以將FIFO分為同步FIFO和異步FIFO。同步FIFO是指讀時鐘和寫時鐘為同一個時鐘,在時鐘沿來臨時同時發生讀寫操作;異步FIFO是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。對于異步FIFO一般有兩種理解,一種是讀寫操作不使用時鐘,而是直接采用wr_en(Write Enabled)和rd_en(Read Enabled)來進行控制;另一種,是指在FPGA和ASIC設計中,異步FIFO具有兩個時鐘的雙口FIFO,?讀些操作在各自的時鐘延上進行,在兩個不同時鐘下,可以同時進行讀或寫。異步FIFO在FPGA設計匯總占用的資源比同步FIFO大很多,所以盡量采用同步FIFO設計。然而對于ARM?系統內絕大部分外設接口都是異步?FIFO。
4.FIFO數據緩存器設計的難點在于怎樣判斷FIFO的空/滿狀態。
假設一個深度為128字節的FIFO怎樣工作(使用已轉換為二進制的指針)。FIFO_WIDTH=8,FIFO_DEPTH= 2^N = 128,N = 7,指針寬度為N+1=8。起初rd_ptr_bin和wr_ptr_bin均為“00000000”。此時FIFO中寫入128個字節的數據。wr_ptr_bin =“10000000”,rd_ptr_bin=“00000000”。當然,這就是滿條件。現在,假設執行了8次的讀操作,使得rd_ptr_bin =“10000000”,這就是空條件。
本文用數據結構的方式來完成FIFO緩沖。
1.緩存區的制作
思路:按照輸入數據的順序輸出數據
簡單地數據存儲:
struct FIFO_BUF{unsigned char data[128];int next; }fifo_buf;unsigned char buffer[128]={0};//用buffer模擬輸入數據void init_write_to_buf(void) {if(fifo_buf.next<128){fifo_buf.data[fifo_buf.next]=buffer[fifo_buf.next]; //將buffer中的數據寫入到fifo_buf中;fifo_buf.next++;}return 0; }fifo_buf.next的起始點是0,所以最初存儲的數據是fifo_buf.data[0],下一個是fifo_buf.data[1],依次類推,一共128個存儲位置。
下一個存儲位置有用變量next管理,這樣就可以記住128數據而不溢出,為保險next變為128(即127已經寫了)之后就不要了。
簡單地數據讀取:
void read_from_fifo_buf(void) {for(;;){if(fifo_buf.next==0)return; //為0表示沒有數據,不用讀取else{i = fifo_buf.data[0]; //讀取第0個位置的元素;fifo_buf.next--;for(int j=0;j<fifo_buf.next;j++)//將后面的數據往前移{fifo_buf.data[j] = fifo_buf.data[j+1];}}} }如果next不是0,說明至少有一個數據,最開始的一個數據肯定放在data[0]中,將這個數據放入到變量i中,這樣數據就減少了一個,所以next要減去1;
接下來的for語句工作原理如下:
數據存放的位置全部都向前移動了一個位置。如果不移送的話,下次就不能從data[0]讀入數據了。
此方法的缺點:移送數據的處理,一般不會超過3個,且基本上沒問題。有時需要移送上百個數據,就會出現問題。
2 改善上述緩存區
思路:維護下一個要寫入數據的位置和下一個要讀出數據的位置。就像數據讀出位置追著數據寫入位置跑一樣。這樣就不需要數據移送操作了。數據讀出位置追上數據寫入位置的時候,就相當于緩存區為空,沒有數據。
但是這樣的緩存區使用一段時間后,下一個數據寫入位置會變為31,而這時下一個數據讀出位置可能已經是29或者30了。若干次后,當下一個寫入位置變為128的時候,就沒地方可以寫入數據了。
如果當下一個數據寫入位置到達緩存區終點時,數據讀出位置也恰好到達緩沖區終點,也就是緩沖區正好變為空,這種情況我們只要將下一個數據寫入位置和下一個數據讀出位置都再置位0就行了,就像轉回去從頭再來一樣。
但是總還是會有數據讀出位置沒有追上數據寫入位置的情況。這時又不得不進行數據移送操作。
3 緩存區的實現
當下一個數據寫入位置到達緩沖區最末尾時,緩存區開頭部分應該已經變空了(如果還沒有變空,說明數據讀出跟不上數據寫入,只能把部分數據扔掉了)。因此如果下一個數據寫入位置到了128以后,就強制性的將它設置為0。這樣寫一個數據寫入位置就跑到了下一個數據讀出位置的后面。
對下一個數據讀出位置也做同樣的處理。一旦到了128以后,就把它設置為從0開始繼續讀取數據。這樣128字節的緩存區就能一圈一圈地不停循環,長久使用。數據移送操作一次都不需要。這個緩存區雖然只有128字節,只要不溢出的話,他就能持續使用下去。
數據的寫入
struct FIFO_BUF{unsigned char data[128];int next_w; //寫位置標志int next_r; //讀位置標志int len; //數據長度 }fifo_buf; void write_to_fifo_buf(void) {if(fifo_buf.len>=128)return;else{fifo_buf.data = data; //將一個數據寫入到緩存中fifo_buf.next_w++;fifo_buf.len++;if(fifo_buf.next_w==128)fifo_buf.next_w=0;}return 0; }數據的讀取
void read_from_fifo_buf(void) {for(;;){i = fifo_buf.data[fifo_buf.next_r]; //讀取next_r處的數據fifo_buf.len--;fifo_buf.next_r++;if(fifo_buf.next_r==128){fifo_buf.next_r=0;}} }4 FIFO緩存區最終實現
struct FIFO_BUF {unsigned char *buf;int fifo_w;int fifo_r;int len; //緩存區總的字節數int cache_data; //用于保存緩存區里沒有數據的字節數int flags; }fifo_buf;初始化
void fifo_init(fifo_buf *fifo,int len,unsigned char *buf) {fifo->buf = buf;fifo->len = len;fifo->cache_data = len;fifo->fifo_w = 0;fifo->fifo_r = 0;fifo->flags = 0;return; }寫
#define FLAGS_OVERRUN 0x00000001int fifo_write(struct fifo_buf *fifo,unsigned char data) {if(fifo->cache_data == 0) //空余沒有了,溢出{fifo->flags |= FLAGS_OVERRUN;return -1;}fifo->buf[fifo->fifo_w]=data;fifo->fifo_w++;if(fifo->fifo_w==fifo->len){fifo->fifo_w = 0;}fifo->cache_data--;return 0; }讀
int fifo_read(fifo_buf *fifo,unsigned char buffer) {if(fifo->cache_data == fifo->len){return -1;}else{buffer = fifo->buf[fifo->fifo_r]; //一個數據寫入到buffer中fifo->fifo_r++;if(fifo->fifo_r==32){fifo->fifo_r = 0;}fifo->cache_data++;}return buffer; }查詢寫入了多少數據?
int fifo_status(void) {return fifo->len - fifo->cache_data; }總結
- 上一篇: 小学毕业java_Java运算符
- 下一篇: hadoop原来是这样的!