日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

vivado 验证ddr引脚_vivado下ddr3的读写和测试详解

發布時間:2024/4/13 编程问答 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vivado 验证ddr引脚_vivado下ddr3的读写和测试详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近博主在根據例程做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的時序重要的還是這兩點,其他的至于如何精確地址和數據對應,可以具體分析,會發現程序寫的還是很嚴謹的啊。。。

總結

以上是生活随笔為你收集整理的vivado 验证ddr引脚_vivado下ddr3的读写和测试详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。