內容
FIFO緩存是介于兩個子系統之間的彈性存儲器,其概念圖如圖1所示。它有兩個控制信號,wr和rd,用于讀操作和寫操作。當wr被插入時,輸入的數據被寫入緩存,此時讀操作被忽視。FIFO緩存的head一般情況下總是有效的,因此可在任意時間被讀取。rd信號實際上就像“remove”信號;當其被插入的時候,FIFO緩存的第一個項(即head)被移除,下一個項變為可用項。
?
圖1 FIFO緩存的概念框圖
在許多應用中,FIFO緩存是一種臨界組件,其實現的優化相當復雜。在本節中,我們介紹一種簡單的、真實的基于循環序列設計的FIFO緩存。更有效的、基于指定器件實現的FIFO緩存可在Altera或Xilinx的相關手冊中找到。
基于循環隊列的實現
一種實現FIFO緩存的方法是給寄存器文件添加一個控制電路。寄存器文件通過兩個指針像循環隊列一樣來排列寄存器。寫指針(write poniter)指向隊列的頭(head);讀指針(read poniter)指向隊列的尾(tail)。每次讀操作或寫操作,指針都會前進一個位置。8-字循環隊列的操作如圖2所示。
圖2 基于循環隊列的FIFO緩存
FIFO緩存通常包括兩個標志信號,full和empty,相應地來指示FIFO滿(即不可寫)或FIFO空(即不可讀)。這兩種情況發生在讀指針和寫指針相等的時候,如圖2(a)、(f)和(i)所示的情況。控制器最難的設計任務是獲取一種分辨這兩種情形的機制。一種方案是使用觸發器來跟蹤empty和full標志。當系統被初始化時,觸發器被設置為1和0;然后在每一個時鐘周期根據wr和rd的值來修改。
代碼 FIFO緩存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
module?fifo #( ??parameter?B=8, // number of bits in a word ????????????W=3? // number of address bits ) ( ??// global clock and aysn reset ??input?clk, ??input?rst_n, ??// fifo interface ??//? fifo control signnal ??input?rd, ??input?wr, ??//? fifo status signal ??output?empty, ??output?full, ??// fifo data bus ??input?[B-1:0] w_data, ??output?[B-1:0] r_data ); // signal declaration reg?[B-1:0] array_reg [2**W-1:0];? // register array reg?[W-1:0] w_ptr_reg, w_ptr_next, w_ptr_succ; reg?[W-1:0] r_ptr_reg, r_ptr_next, r_ptr_succ; reg?full_reg, empty_reg, full_next, empty_next; wire?wr_en; // body // register file write operation always?@(posedge?clk) ??if?(wr_en) ????array_reg[w_ptr_reg] <= w_data; // register file read operation assign?r_data = array_reg[r_ptr_reg]; // write enabled only when FIFO is not full assign?wr_en = wr & ~full_reg; // fifo control logic // register for read and write pointers always?@(posedge?clk, negedge?rst_n) ??if?(!rst_n) ??begin ????w_ptr_reg <= 0; ????r_ptr_reg <= 0; ????full_reg <= 1'b0; ????empty_reg <= 1'b1; ??end ??else ??begin ????w_ptr_reg <= w_ptr_next; ????r_ptr_reg <= r_ptr_next; ????full_reg <= full_next; ????empty_reg <= empty_next; ??end // next-state logic for read and write pointers always?@* begin ??// successive pointer values ??w_ptr_succ = w_ptr_reg + 1; ??r_ptr_succ = r_ptr_reg + 1; ??// default: keep old values ??w_ptr_next = w_ptr_reg; ??r_ptr_next = r_ptr_reg; ??full_next = full_reg; ??empty_next = empty_reg; ??case?({wr, rd}) ????// 2'b00:? no op ????2'b01: // read ??????if?(~empty_reg) // not empty ??????begin ????????r_ptr_next = r_ptr_succ; ????????full_next = 1'b0; ????????if?(r_ptr_succ==w_ptr_reg) ??????????empty_next = 1'b1; ??????end ????2'b10: // write ????if?(~full_reg) // not full ????begin ??????w_ptr_next = w_ptr_succ; ??????empty_next = 1'b0; ??????if?(w_ptr_succ==r_ptr_reg) ????????full_next = 1'b1; ????end ????2'b11: // write and read ????begin ??????w_ptr_next = w_ptr_succ; ??????r_ptr_next = r_ptr_succ; ????end ??endcase end // output assign?full = full_reg; assign?empty = empty_reg; endmodule |
代碼被分為寄存器文件和FIFO控制器兩部分。控制器由兩個指針和兩個標志觸發器組成,它們的次態邏輯會檢測wr和rd信號,以采取相應的動作。舉例說,在“10”條件下,即暗示只發生寫操作。先檢查標志觸發器,以確保緩存不為滿。如果滿足條件,我們將寫指針前進一位,并清除空標志。再多存儲一個字(偏移地址為1所對應的數據)可能使得FIFO緩存滿,即新的寫指針趕上了讀指針,我們使用w_ptr_succ==r_ptr_reg表達式來描述這一情況。
根據圖2,我寫了下面的testbench,其RTL仿真結果與圖2一致。
代碼 FIFO緩存的testbench
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
`timescale?1ns/1ns module?fifo_tb; localparam T=20; // clock period // global clock and asyn reset reg?clk, rst_n; // fifo interface reg?rd, wr; wire?empty, full; reg?[7:0] w_data; wire?[7:0] r_data; // fifo instantiation fifo #(.B(8), .W(3)) fifo_inst ( ??.clk(clk), .rst_n(rst_n), ??.rd(rd), .wr(wr), ??.empty(empty), .full(full), ??.w_data(w_data), .r_data(r_data) ); // clcok always begin ??clk = 1'b0; ??#(T/2); ??clk = 1'b1; ??#(T/2); end // reset initial begin ??rst_n = 1'b0; ??#(T/2) ??rst_n = 1'b1; end // stimulus body initial begin ??// initial input; empty ??rd=0; wr=0; w_data=8'h00; ??@(posedge?rst_n); // wait to deassert rst_n ??@(negedge?clk); // wait for a clock ??// 1 write ??wr=1; w_data=8'h11; ??@(negedge?clk); // wait to assert wr ??wr=0; ??@(negedge?clk); // wait to deassert wr ??// 3 writes ??wr=1; ??repeat(3) ??begin ????w_data=w_data+8'h11; ????@(negedge?clk); ??end ??wr=0; ??@(negedge?clk); ??// 1 read ??rd=1; ??@(negedge?clk); // wait to assert rd ??rd=0; ??@(negedge?clk) // wait to deassert rd ??// 4 writes ??wr=1; ??repeat(4) ??begin ????w_data=w_data+8'h11; ????@(negedge?clk); ??end ??wr=0; ??@(negedge?clk); ??// 1 write; full ??wr=1; w_data=8'hAA; ??@(negedge?clk); ??wr=0; ??@(negedge?clk); ??// 2 reads ??rd=1; ??repeat(2) @(negedge?clk); ??rd=0; ??@(negedge?clk); ??// 5 reads ??rd=1; ??repeat(5) @(negedge?clk); ??rd=0; ??@(negedge?clk); ??// 1 read; empty ??rd=1; ??@(negedge?clk); ??rd=0; ??@(negedge?clk); ??$stop; end endmodule |
圖3 RTL級仿真波形
審核編輯:劉清
評論
查看更多