XILINX-DDR3IP核的使用

最近博主在根據例程做ddr3的讀寫測試,發現根本看不到好吧,雖然之前博主做過SDRAM的讀寫測試,但是ddr3更加複雜,時序寫起來很喫力,所以需要用到vivado下自帶的ip核。具體來看下面例化過程:

1.在ip核下搜索mig 雙擊打開

2.直接next  然後在當前界面修改你的ddr3ip核的名字

這裏博主是因爲已經例化了ip核,所以名字不能修改,然後next

3.這是要不要兼容芯片,不選,點擊next

4.勾選你的存儲器類型,我的是ddr3,點擊next

5.

這個配置比較多,第一個時鐘爲ddr3實際工作的時鐘,然後選擇你的內存型號,數據寬度即可,點擊next

6.

然後輸入時鐘可能需要pll倍頻得到,一般是200Mhz,這裏注意看下最後一行的用戶地址類型,它是由bank+row+column組成的,這個在後面的讀寫測試會進一步提到。

7.

時鐘選擇不差分,然後參考時鐘爲用戶時鐘。

8.下面就是默認next,然後就是分配管腳了,這個你買的開發板一般都會提高ucf文件,直接複製就行。

然後next,生成。

以上就是ip核的簡單例化過程,這個步驟網上有很多類似的,博主就不一一講解了,把精力放在讀寫測試這塊。

首先來看老三樣:ip核用戶界面下的控制命令,讀和寫

這是控制命令,可以讓用戶來發送讀或者寫命令,需要注意的事只有當app_rdy和app_en同爲高時纔有效,命令被髮出。這裏博主通過ila上電分析發現app_rdy爲ip核自己產生的輸出信號,但是它並不是一直都是高電平,所以在後續的讀寫測試時需要判斷,至於怎麼判斷,我們後面代加上電分析。

上面是寫命令,可以看到當add_wdf_wren和add_wdf_end同爲高時數據纔能有效被寫進去,同時app_wdf_rdy也要爲高。需要注意的一點是,寫數據和寫命令此時不再有關係,爲什麼,因爲寫數據其實是通過fifo緩存,當寫命令有效時,由於先進先出的特性會把它所對應數據給寫入,當然這個很拗口,下面會給出示例

上面的是讀過程,可以看出當讀命令發出後需要一個延遲讀數據纔會有效。

下面來看代碼進行講解:

module mem_burst
#(
    parameter MEM_DATA_BITS = 64,
    parameter ADDR_BITS = 24
)
(
    input rst,                                 /*復位*/
    input mem_clk,                               /*接口時鐘*/
    input rd_burst_req,                          /*讀請求*/
    input wr_burst_req,                          /*寫請求*/
    input[9:0] rd_burst_len,                     /*讀數據長度*/
    input[9:0] wr_burst_len,                     /*寫數據長度*/
    input[ADDR_BITS - 1:0] rd_burst_addr,        /*讀首地址*/
    input[ADDR_BITS - 1:0] wr_burst_addr,        /*寫首地址*/
    output rd_burst_data_valid,                  /*讀出數據有效*/
    output wr_burst_data_req,                    /*寫數據信號*/
    output[MEM_DATA_BITS - 1:0] rd_burst_data,   /*讀出的數據*/
    input[MEM_DATA_BITS - 1:0] wr_burst_data,    /*寫入的數據*/
    output rd_burst_finish,                      /*讀完成*/
    output wr_burst_finish,                      /*寫完成*/
    output burst_finish,                         /*讀或寫完成*/
    
    ///////////////////
   output[ADDR_BITS-1:0]                       app_addr,
   output[2:0]                                 app_cmd,
   output                                      app_en,
   output [MEM_DATA_BITS-1:0]                  app_wdf_data,
   output                                      app_wdf_end,
   output [MEM_DATA_BITS/8-1:0]                app_wdf_mask,
   output                                      app_wdf_wren,
   input [MEM_DATA_BITS-1:0]                   app_rd_data,
   input                                       app_rd_data_end,
   input                                       app_rd_data_valid,
   input                                       app_rdy,
   input                                       app_wdf_rdy,
   input                                       ui_clk_sync_rst,  
   input                                       init_calib_complete
);
 
assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};
 
localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_READ_WAIT = 3'd2;
localparam MEM_WRITE  = 3'd3;
localparam MEM_WRITE_WAIT = 3'd4;
localparam READ_END = 3'd5;
localparam WRITE_END = 3'd6;
localparam MEM_WRITE_FIRST_READ = 3'd7;
 
/*parameter IDLE = 3'd0;
parameter MEM_READ = 3'd1;
parameter MEM_READ_WAIT = 3'd2;
parameter MEM_WRITE  = 3'd3;
parameter MEM_WRITE_WAIT = 3'd4;
parameter READ_END = 3'd5;
parameter WRITE_END = 3'd6;
parameter MEM_WRITE_FIRST_READ = 3'd7;*/
reg[2:0] state;    
reg[9:0] rd_addr_cnt;
reg[9:0] rd_data_cnt;
reg[9:0] wr_addr_cnt;
reg[9:0] wr_data_cnt;
 
reg[2:0] app_cmd_r;
reg[ADDR_BITS-1:0] app_addr_r;
reg app_en_r;
reg app_wdf_end_r;
reg app_wdf_wren_r;
assign app_cmd = app_cmd_r;
assign app_addr = app_addr_r;
assign app_en = app_en_r;
assign app_wdf_end = app_wdf_end_r;
assign app_wdf_data = wr_burst_data;
assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
assign rd_burst_finish = (state == READ_END);
assign wr_burst_finish = (state == WRITE_END);
assign burst_finish = rd_burst_finish | wr_burst_finish;
 
assign rd_burst_data = app_rd_data;
assign rd_burst_data_valid = app_rd_data_valid;
 
assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;
 
always@(posedge mem_clk or posedge rst)
begin
    if(rst)
    begin
        app_wdf_wren_r <= 1'b0;
    end
    else if(app_wdf_rdy)
        app_wdf_wren_r <= wr_burst_data_req;
end
 
always@(posedge mem_clk or posedge rst)
begin
    if(rst)
    begin
        state <= IDLE;
        app_cmd_r <= 3'b000;
        app_addr_r <= 0;
        app_en_r <= 1'b0;
        rd_addr_cnt <= 0;
        rd_data_cnt <= 0;
        wr_addr_cnt <= 0;
        wr_data_cnt <= 0;
        app_wdf_end_r <= 1'b0;
    end
    else if(init_calib_complete ===  1'b1)
    begin
        case(state)
            IDLE:
            begin
                if(rd_burst_req)
                begin
                    state <= MEM_READ;
                    app_cmd_r <= 3'b001;
                    app_addr_r <= {rd_burst_addr,3'd0};
                    app_en_r <= 1'b1;
                end
                else if(wr_burst_req)
                begin
                    state <= MEM_WRITE;
                    app_cmd_r <= 3'b000;
                    app_addr_r <= {wr_burst_addr,3'd0};
                    app_en_r <= 1'b1;
                    wr_addr_cnt <= 0;
                    app_wdf_end_r <= 1'b1;
                    wr_data_cnt <= 0;
                end
            end
            MEM_READ:
            begin
                if(app_rdy)
                begin
                    app_addr_r <= app_addr_r + 8;
                    if(rd_addr_cnt == rd_burst_len - 1)
                    begin
                        state <= MEM_READ_WAIT;
                        rd_addr_cnt <= 0;
                        app_en_r <= 1'b0;
                    end
                    else
                        rd_addr_cnt <= rd_addr_cnt + 1;
                end
                
                if(app_rd_data_valid)
                begin
                    //app_addr_r <= app_addr_r + 8;
                    if(rd_data_cnt == rd_burst_len - 1)
                    begin
                        rd_data_cnt <= 0;
                        state <= READ_END;
                    end
                    else
                    begin
                        rd_data_cnt <= rd_data_cnt + 1;
                    end
                end
            end
            MEM_READ_WAIT:
            begin
                if(app_rd_data_valid)
                begin
                    if(rd_data_cnt == rd_burst_len - 1)
                    begin
                        rd_data_cnt <= 0;
                        state <= READ_END;
                    end
                    else
                    begin
                        rd_data_cnt <= rd_data_cnt + 1;
                    end
                end
            end
            MEM_WRITE_FIRST_READ:
            begin
                app_en_r <= 1'b1;
                state <= MEM_WRITE;
                wr_addr_cnt <= 0;
            end
            MEM_WRITE:
            begin
                if(app_rdy)
                begin
                    app_addr_r <= app_addr_r + 8;
                    if(wr_addr_cnt == wr_burst_len - 1)
                    begin
                        app_wdf_end_r <= 1'b0;
                        app_en_r <= 1'b0;
                    end
                    else
                    begin
                        wr_addr_cnt <= wr_addr_cnt + 1;
                    end
                end
                    
                if(wr_burst_data_req)
                begin
                    //app_addr_r <= app_addr_r + 8;
                    if(wr_data_cnt == wr_burst_len - 1)
                    begin    
                        state <= MEM_WRITE_WAIT;
                    end
                    else
                    begin
                        wr_data_cnt <= wr_data_cnt + 1;
                    end
                end
                
            end
            READ_END:
                state <= IDLE;
            MEM_WRITE_WAIT:
            begin
                if(app_rdy)
                begin
                    app_addr_r <= app_addr_r + 'b1000;
                    if(wr_addr_cnt == wr_burst_len - 1)
                    begin
                        app_wdf_end_r <= 1'b0;
                        app_en_r <= 1'b0;
                        if(app_wdf_rdy) 
                            state <= WRITE_END;
                    end
                    else
                    begin
                        wr_addr_cnt <= wr_addr_cnt + 1;
                    end
                end
                else if(~app_en_r & app_wdf_rdy)
                    state <= WRITE_END;
                
            end
            WRITE_END:
                state <= IDLE;
            default:
                state <= IDLE;
        endcase
    end
end
endmodule


這個是黑金給的例程,一開始沒看懂,搞了好幾天纔看懂整個細節,下面來講解一下:首先state在IDLE狀態,當wr_burst_req有效時進入MEM_WRITE狀態,這時候有兩個條件判斷,第一個if(app_rdy)爲真,說明寫命令是有效的,那麼隨之伴隨的是地址的累加,同時也會計數,如果寫命令發送了128次,就結束。第二個if(wr_burst_data_req)爲真,注意wr_burst_data_req爲真實際就是app_wdf_rdy爲真,所以寫的數據是被緩存到了fifo並且當讀命令有效時會依次傳入,這裏大家會問,爲啥不讓app_rdy和app_wdf_rdy同時爲真才地址增加和寫數據呀,這是因爲app_rdy和app_wdf_rdy並不是一直都爲高電平,下面是上電結果;

看到沒,rdy爲低時,app_wdf_rdy爲高,這說明數據此時相對於地址來說多寫進去一次,那麼多的那個數據就被緩存了,等到下一個rdy爲高就會去寫入之前那個緩存的數據而不是當前時刻的數據。這也就是爲什麼每個條件判斷語句都會去計數,一個計的是多少個寫命令被髮出,另一個是多少個寫的數據被髮送。

下面來看下讀過程,首先state在IDLE狀態,當rd_burst_req有效時進入MEM_READ狀態,這裏同樣有兩個if判斷,第一個if(app_rdy)是用來判斷讀命令是否有效並且地址累加,第二個if(app_rd_data_valid)是讀數據有效,根據上面的讀流程,讀數據有效並不會隨着讀命令有效就馬上出現,一般會延遲多個週期,所以同樣需要分開判斷並且計數。來看時序:

看到沒,當讀請求有效時,下一個時鐘週期地址就開始計數並且累計了,但是app_rd_data_valid還需延遲一會纔能有效。

其實把讀寫原理搞懂後就很簡單,博主一開始卡住的地方就是寫的那塊,以爲寫數據需要app_rdy和app_wdf_rdy同時有效才能成功寫入,沒有搞懂命令和數據的關係,因爲ip核的寫數據是先緩存在fifo中的,所以即使當前寫命令無效時,寫數據依舊可以成功寫入。感覺是不是和SDRAM不一樣啊,可能沒用ip核和用了還是有區別的吧。。。

感覺ddr3的時序重要的還是這兩點,其他的至於如何精確地址和數據對應,可以具體分析,會發現程序寫的還是很嚴謹的啊。。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章