FPGA_FIFO深度與寬度的配置及驗證

fifo是先進先出的存儲器,在FPGA中應用於跨時鐘域的情景,這次實驗用於記載fifo的深度與寬度的配置及驗證過程。異步

實驗大體流程:ide

在fifo_wr模塊中以wr_en時鐘向FIFO存儲器寫入一組數,經過fifo_rd模塊以rd_en時鐘讀出這組數據並向串口發送這組數據。
先用用Quartus II生成FIFO_IP核:spa

箭頭1:設置FIFO的位寬,這裏咱們選擇8bits。
箭頭2:設置FIFO的深度,也就是能存放多少個指定位寬的數據,這裏咱們選擇256words,這樣設置     之後FIFO的容量大小爲256個8bits。

箭頭3:用於選擇單時鐘FIFO
箭頭4:用於選擇雙時鐘FIFO。
箭頭5:選擇不一樣的輸出位寬(僅在雙時鐘時可選)。
這次實驗咱們選擇雙時鐘FIFO,這時在箭頭5處能夠選擇不一樣的輸出位寬。這裏咱們採用默認方式輸出數據與輸入數據等寬。

跳到DCFIFO 2這裏:code

rdfull和wrfull:FIFO滿的標記信號,爲高電平時表示FIFO已滿,此時不能再進行寫操做。

rdempty和wrempty:FIFO空的標記信號,爲高電平時表示FIFO已空,此時不能在進行讀操做。

rdusedw[]和wrusedw[]:顯示存儲在FIFO中數據個數的信號。

Add an extra MSB to usedw ports:將rdusedw和wrusedw數據位寬增長1位,用於保護FIFO在寫滿時不會翻轉到0。

Asynchronous clear:異步復位信號,用於清空FIFO。

這裏我選擇輸出讀空、讀滿、寫空、寫滿等信號以備後面實驗。到這裏FIFO的配置已經完成了,其他配置選擇默認便可,下面還須要一個UART串口發送的模塊來配合實驗。blog

//串口發送模塊

module    uart_send
(
   input    sys_clk  ,            //50Mhz系統時鐘
   input    sys_rst_n,            //系統復位,低有效
    input    uart_en,             //發送使能信號
    input    [7:0]    uart_din,   //待發送數據
    input    rdempty,             //fifo讀空標誌
    output  reg  uart_txd,        //發送數據
    output  reg  led              //指示燈
);
`
parameter  CLK_FREQ = 50000000;                    //系統時鐘頻率
parameter  UART_BPS = 115200;                      //串口波特率
localparam BPS_CNT  = CLK_FREQ/UART_BPS;           //對系統時鐘計數BPS_CNT次以獲得指定波特率

reg  [15:0] clk_cnt;       //系統時鐘計數器
reg  [7:0] uart_data;      //接收的數據
reg       uart_done;       //接收一幀數據完成標誌信號

//reg define
reg        uart_en_d0; 
reg        uart_en_d1;  
reg [ 3:0] tx_cnt;                    //發送數據計數器
reg        tx_flag;                   //發送過程標誌信號
reg [ 7:0] tx_data;                   //寄存發送數據
reg  [31:0] cnt;                      //時鐘計數器
reg   tx_delay;//wire define
wire       en_flag;

wire            wrreq   ;         // 寫請求信號
wire    [7:0]   data    ;         // 寫入FIFO的數據
wire            wrempty ;         // 寫側空信號
wire            wrfull  ;         // 寫側滿信號
wire            wrusedw ;         // 寫側FIFO中的數據量

wire            rdreq   ;         // 讀請求信號
wire    [7:0]   q       ;         // 從FIFO輸出的數據
//wire            rdempty ;         // 讀側空信號
wire            rdfull  ;         // 讀側滿信號
wire            rdusedw ;         // 讀側FIFO中的數據量

//*****************************************************
//**                    main code
//*****************************************************
assign en_flag = (~uart_en_d1) & uart_en_d0;    //& (~tx_delay);//消抖//按鍵消抖300ms
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt <= 0;
    else if(cnt >= 31'd15_000_000) begin
        led <= 0;
        cnt <= 0;
        tx_delay <= 0;
    end
    else if(tx_flag == 1) begin
        tx_delay <= 1;
        led <= 1;
    end
    else if(tx_delay == 1)
        cnt <= cnt + 1'b1;
end//對發送使能信號uart_en延遲兩個時鐘週期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        uart_en_d0 <= 1'b0;                                  
        uart_en_d1 <= 1'b0;
    end                                                      
    else begin

        uart_en_d0 <= uart_en;                               
        uart_en_d1 <= uart_en_d0;                            
    end
end
//當脈衝信號en_flag到達時,寄存待發送的數據,並進入發送過程          
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin                                  
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
    end 
    else if (en_flag) begin                 //檢測到發送使能上升沿                      
            tx_flag <= 1'b1;                //進入發送過程,標誌位tx_flag拉高
            tx_data <= uart_din;            //寄存待發送的數據
        end
        else 
        if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
        begin                               //計數到中止位中間時,中止發送過程
            tx_flag <= 1'b0;                //發送過程結束,標誌位tx_flag拉低
            tx_data <= 8'd0;
        end
        else begin
            tx_flag <= tx_flag;
            tx_data <= tx_data;
        end 
end

//進入發送過程後,啓動系統時鐘計數器與發送數據計數器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin                             
        clk_cnt <= 16'd0;                                  
        tx_cnt  <= 4'd0;
    end                                                      
    else if (tx_flag) begin                 //處於發送過程
        if (clk_cnt < BPS_CNT - 1) begin
            clk_cnt <= clk_cnt + 1'b1;
            tx_cnt  <= tx_cnt;
        end
        else begin
            clk_cnt <= 16'd0;               //對系統時鐘計數達一個波特率週期後清零
            tx_cnt  <= tx_cnt + 1'b1;       //此時發送數據計數器加1
        end
    end
    else begin                              //發送過程結束
        clk_cnt <= 16'd0;
        tx_cnt  <= 4'd0;
    end
end

//根據發送數據計數器來給uart發送端口賦值
always @(posedge sys_clk or negedge sys_rst_n) begin        
    if (!sys_rst_n)  
        uart_txd <= 1'b1;        
    else if (tx_flag&&~rdempty)
        case(tx_cnt)
            4'd0: uart_txd <= 1'b0;         //起始位 
            4'd1: uart_txd <= tx_data[0];   //數據位最低位
            4'd2: uart_txd <= tx_data[1];
            4'd3: uart_txd <= tx_data[2];
            4'd4: uart_txd <= tx_data[3];
            4'd5: uart_txd <= tx_data[4];
            4'd6: uart_txd <= tx_data[5];
            4'd7: uart_txd <= tx_data[6];
            4'd8: uart_txd <= tx_data[7];   //數據位最高位
            4'd9: uart_txd <= 1'b1;         //中止位
            default: ;
        endcase
     else if(tx_flag&&rdempty)              //讀區爲空,輸出8'hCC
            case(tx_cnt)
            4'd0: uart_txd <= 1'b0;         //起始位 
            4'd1: uart_txd <= 1'b0;         //數據位最低位
            4'd2: uart_txd <= 1'b0;
            4'd3: uart_txd <= 1'b1;
            4'd4: uart_txd <= 1'b1;
            4'd5: uart_txd <= 1'b0;
            4'd6: uart_txd <= 1'b0;
            4'd7: uart_txd <= 1'b1;
            4'd8: uart_txd <= 1'b1;         //數據位最高位
            4'd9: uart_txd <= 1'b1;         //中止位
            default: ;
        endcase
    else 
        uart_txd <= 1'b1;                   //空閒時發送端口爲高電平
end

endmodule
串口發送模塊

 在這裏說明一下上面代碼,當讀空信號成立時串口會發送:16進制數CC。另外還須要讀寫模塊,以下:rem

//寫FIFO模塊
module fifo_wr(
    //mudule clock
    input                   clk    ,        // 時鐘信號
    input                   rst_n  ,        // 復位信號
    //user interface
    input                   wrempty,        // 寫空信號
    input                   wrfull ,        // 寫滿信號
    output    reg  [7:0]    data   ,        // 寫入FIFO的數據
    output    reg           wrreq           // 寫請求
);

//reg define
reg   [1:0]         flow_cnt;               // 狀態流轉計數
reg   [31:0]        delay_a;

//*****************************************************
//**                    main code
//*****************************************************

//向FIFO中寫入數據
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
         wrreq <= 1'b0;
            delay_a <= 1'b0;
            data <= 1'b0;
         flow_cnt <= 2'd0;
    end
    else if(delay_a == 31'd4) begin
        delay_a <= 1'b0;
        data <= data + 1'b1;
    end
    else begin
        case(flow_cnt)
            2'd0: begin 
                if(wrempty) begin     //寫空時,寫請求拉高,跳到下一個狀態
                    wrreq <= 1'b1;
                    flow_cnt <= flow_cnt + 1'b1;
                end 
                else
                    flow_cnt <= flow_cnt;
            end 
            2'd1: begin               //寫滿時,寫請求拉低,跳回上一個狀態
                if(wrfull) begin
                    wrreq <= 1'b0;
                    flow_cnt <= 2'd0;
                end
                else begin            //沒有寫滿的時候,寫請求拉高,繼續輸入數據
                    wrreq <= 1'b1;
                    delay_a <= delay_a + 1'b1;
                end
            end 
            default: flow_cnt <= 2'd0;
        endcase
    end
end


endmodule
fifo寫模塊
//讀FIFO模塊

module fifo_rd(
    //system clock
    input                    clk    ,        // 時鐘信號
    input                    rst_n  ,        // 復位信號(低有效)

    //user interface
    input           [7:0]    data   ,        // 從FIFO輸出的數據
    input                    rdfull ,        // 讀滿信號
    input                    rdempty,        // 讀空信號
    output   reg             rdreq          // 讀請求
);

//reg define
reg   [7:0]                  data_fifo;      // 讀取的FIFO數據
reg   [1:0]                  flow_cnt ;      // 狀態流轉計數

//*****************************************************
//**                    main code
//*****************************************************

     
//從FIFO中讀取數據
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rdreq <= 1'b0;
        data_fifo <= 8'd0;
    end
    else begin
        case(flow_cnt)
            2'd0: begin
                if(rdfull) begin
                    rdreq <= 1'b1;
                    flow_cnt <= flow_cnt + 1'b1;
                end
                else
                    flow_cnt <= flow_cnt;
            end 
            2'd1: begin
                if(rdempty) begin
                    rdreq <= 1'b0;
                    data_fifo <= 8'd0;
                    flow_cnt  <= 2'd0;
                end
                else begin
                    rdreq <= 1'b1;
                    data_fifo <= data;
                end 
            end 
            default: flow_cnt <= 2'd0;
        endcase
    end
end

endmodule
fifo讀模塊

讀寫模塊須要注意,我是以寫滿和寫空爲寫操做的判斷基準的,讀操做的判斷也是同樣的,固然正常使用FIFO是不能這樣作的,這次僅僅爲了驗證FIFO的寬度和深度。input

如下是頂層模塊,其中鎖相環PLL並無用到:it

//fifo串口消息發送


module    STM32_UART
(
   input    sys_clk  ,    //50Mhz系統時鐘
   input    sys_rst_n,    //系統復位,低有效
    input    uart_en,     //發送使能信號
    output   uart_txd,    //
    input    uart_rxd,    //
    output   reg led      //指示燈
);

//reg define
reg             wr_en;
reg                    rd_en;
reg    [31:0]   cnt;
reg    [31:0]   cnt_rd;
reg    [31:0]   cnt_led/* synthesis preserve */;
reg    [7:0]   data;

//wire define
wire    [7:0]   uart_data_w ;     // 接收到的數據
wire            wrreq   ;         // 寫請求信號
//wire    [7:0]   data    ;         // 寫入FIFO的數據
wire            wrempty ;         // 寫側空信號
wire            wrfull  ;         // 寫側滿信號
wire            wrusedw ;         // 寫側FIFO中的數據量

wire            rdreq   ;         // 讀請求信號
wire    [3:0]   q       ;         // 從FIFO輸出的數據
wire            rdempty ;         // 讀側空信號
wire            rdfull  ;         // 讀側滿信號
wire            rdusedw ;         // 讀側FIFO中的數據量

wire            clk_0/*synthesis keep*/;
wire            clk_1/*synthesis keep*/;



always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        led <= 1'b0;
        cnt_led <= 1'b0;
    end
    else if(cnt_led == 5-1) begin
        cnt_led <= 1'b0;
        led <= ~led;
    end
    else begin
        cnt_led <= cnt_led + 1'b1;
        led <= led;
    end
end


always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        wr_en <= 1'b0;
        cnt <= 1'b0;
    end
    else if(cnt == 31'd1_000_000) begin
        cnt <= 1'b0;
        wr_en <= 1'b1;
    end
    else begin
        cnt <= cnt + 1'b1;
        wr_en <= 1'b0;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        rd_en <= 1'b0;
        cnt_rd <= 1'b0;
    end
    else if(cnt_rd == 31'd2_000_000) begin
        cnt_rd <= 1'b0;
        rd_en <= 1'b1;
    end
    else begin
        cnt_rd <= cnt_rd + 1'b1;
        rd_en <= 1'b0;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        data <= 1'b0;
    else if(q != 0&&rdreq)
        data <= q;
    else if(rdreq==0)
        data <= 8'hee;
    else
        data <= 8'hff;
end

uart_send u_uart_send(
    .sys_clk    (sys_clk),
    .sys_rst_n  (sys_rst_n),
     .uart_en    (wr_en),        //發送使能信號
     .rdempty    (rdempty),      //fifo讀空信號
     .uart_din   (data),         //待發送數據
     .uart_txd   (uart_txd),     //發送數據
);

//鎖相環
pll_clk u_pll_clk(
.areset (~sys_rst_n ),           //鎖相環高電平復位,因此復位信號取反
.inclk0 (sys_clk ),
.c0 (clk_0 ),
.c1 (clk_1),
.locked (locked )
);

//例化FIFO模塊
fifo u_fifo(
    .wrclk   ( wr_en ),           // 寫時鐘
    .wrreq   ( wrreq   ),         // 寫請求
    .data    ( uart_data_w    ),         // 寫入FIFO的數據
    .wrempty ( wrempty ),         // 寫空信號
    .wrfull  ( wrfull  ),         // 寫滿信號
    .wrusedw ( wrusedw ),         // 寫側數據量
    
    .rdclk   ( rd_en ),           // 讀時鐘
    .rdreq   ( rdreq   ),         // 讀請求
    .q       ( q       ),         // 從FIFO輸出的數據
    .rdempty ( rdempty ),         // 讀空信號
    .rdfull  ( rdfull  ),         // 讀滿信號
    .rdusedw ( rdusedw )          // 讀側數據量
);

//例化寫FIFO模塊
fifo_wr u_fifo_wr(
    .clk     (wr_en  ),           // 寫時鐘
    .rst_n   (sys_rst_n),         // 復位信號

    .wrreq   (wrreq   ),          // 寫請求
    .data    (uart_data_w    ),          // 寫入FIFO的數據
    .wrempty (wrempty ),          // 寫空信號
    .wrfull  (wrfull  )           // 寫滿信號
);

//例化讀FIFO模塊
fifo_rd u_fifo_rd(
    .clk     (rd_en ),            // 讀時鐘
    .rst_n   (sys_rst_n),         // 復位信號

    .rdreq   (rdreq   ),          // 讀請求
    .data    (q       ),          // 從FIFO輸出的數據
    .rdempty (rdempty ),          // 讀空信號
    .rdfull  (rdfull  )           // 讀滿信號
);

endmodule
頂層模塊

頂層模塊對讀出的數據作了簡單處理,讀出數據爲0時輸出爲FF,讀請求爲0時輸出EE。class

16進制顯示串口打印結果以下:module

下面貼個工程鏈接:

連接:https://pan.baidu.com/s/1S9R0JVtANzVGb4slCgGBMw
提取碼:yfe4

感興趣的能夠試試啦

相關文章
相關標籤/搜索