MIG IP控制DDR3讀寫測試


  本文設計思想采用明德揚至簡設計法。在高速信號處理場合下,很短時間內就要緩存大量的數據,這時片內存儲資源已經遠遠不夠了。DDR SDRAM因其極高的性價比幾乎是每一款中高檔FPGA開發板的首選外部存儲芯片。DDR操作時序非常復雜,之所以在FPGA開發中用途如此廣泛,都要得意於MIG IP核。網上關於MIG控制DDR的資料很多,因此本文只講述個人認為較重要的內容。由於MIG IP核用戶接口時序較復雜,這里給出擴展接口模塊用於進一步簡化接口時序。

  先來看看MIG IP核的架構:

 

  了解下存儲芯片側重要接口:

  ddr_addr             DDR3的行列地址

  ddr_ba                DDR3的bank地址

  ddr_cas_n ddr_ras_n ddr_we_n   命令控制

  ddr_ck ddr_ck_n           差分時鍾

  ddr_dm               數據輸入屏蔽

  ddr_o_dt            片上終端使能,用於使能和禁止片內終端電阻

  ddr_reset_n             DDR3復位

  ddr_dqs ddr_dqs_n         數據同步信號

  ddr_dq                傳輸數據

  之后我們從IP核配置開始說起。Controller Options這頁最為重要,其中包括時鍾策略和外部DDR芯片參數配置。首先時鍾周期選擇為400MHz,此時PHY to Controller Clock Ratio只能是4:1,也就是說MIG用戶側時鍾為100MHz。下半部分是選擇合適的DDR芯片型號和參數,要再三確認無誤。

   Memory Options這頁輸入時鍾周期選擇為200MHz,根據Controller Options頁的選項,該時鍾經過PLL分頻和倍頻后的時鍾分別作為用戶側時鍾100MHz和DDR接口時鍾400MHz。

   這里有個參考時鍾選項,如果Memory Options頁PLL輸入時鍾頻率選為200MHz,此處可以直接選擇Use System Clock,從而簡化接口。

  以上是MIG IP核配置過程中較為重要的部分,實際上上述配置也可通過修改工程代碼中參數來重定義。IP核配置完成,打開example design工程頂層文件,我們來重點關注下用戶側接口功能和時序。

  這是本人寫的注釋,更具體清晰的說明還是要查看官方文檔UG586.接下來看看寫數據和讀數據的接口時序圖(時鍾比例4:1,burst length = 8為例):

  指令通道:

  寫數據:

 

  從時序圖可以看出,指令地址和數據使用兩套時序,彼此相互獨立。為了便於設計,直接將兩套時序嚴格對齊(情況1)也可以正常工作。

  讀數據:

 

  為什么說“時鍾比例4:1,burst length = 8為例”?這一點特別關鍵。此時用戶時鍾周期是DDR接口時鍾周期的4倍,也就是一個用戶時鍾信號上升沿對應8個DDR時鍾邊沿。burst length可以理解為MIG連續操作DDR地址的個數,故在4:1時鍾比例下,一個用戶時鍾周期正好對8個地址進行了讀/寫操作,256bit數據分8次(32bit)寫入DDR中。由此分析,在寫數據時讓app_wdf_end = app_wdf_wren即可,並且讀/寫操作時地址遞增步長為8.

  雖然MIG IP核提供了用戶接口,但讀寫指令通道復用且需要實時關注兩個rdy信號造成了時序操作上的不方便。為此我們需要對接口進一步封裝,保證寫操作時只關注:寫使能user_wdata_en 寫地址user_waddr 寫數據user_wdata和寫准備就緒信號user_wdata_rdy,讀操作時只關注:讀使能user_rdata_en 讀地址user_raddr 讀數據user_rdata 讀數據有效user_rdata_vld和讀操作准備就緒user_rdata_rdy。

  利用擴展接口模塊,將讀通道和寫通道接口分離,並分別例化一個FIFO緩存地址和數據。當讀/寫指令同時有效時,通過MIG側的優先級輪換邏輯輪流讀取其中一個FIFO,每次選一個FIFO讀取直至FIFO為空再重新選擇。其工程結構和核心代碼如下:

  讀側邏輯核心代碼:

 1 //讀側--------------------------------------------------------------
 2 
 3 always @(posedge clk or negedge rst_n )begin 
 4     if(rst_n==0) begin
 5         rd_flag <= (0)  ;
 6     end
 7     else if(rd_flag == 0 && mig_rdy && mig_wdf_rdy && !rdempty1 && (rdempty0 || (!rdempty0 && priority == 0)))begin
 8         rd_flag <= (2'b01)  ;//讀取 寫指令FIFO
 9     end 
10     else if(rd_flag == 0 && mig_rdy && !rdempty0 && (rdempty1 || (!rdempty1 && priority == 1)))begin
11         rd_flag <= (2'b10)  ;//讀取 讀指令FIFO
12     end 
13     else if((rd_flag == 2'b01 && rdempty1)||(rd_flag == 2'b10 && rdempty0))
14         rd_flag <= 0;
15 end
16 
17 //同時非空時輪換優先級
18 always @(posedge clk or negedge rst_n )begin 
19     if(rst_n==0) begin
20         priority <= (0)  ;
21     end
22     else if(rd_flag == 0 && !rdempty0 && !rdempty1)begin
23         priority <= (!priority)  ;
24     end 
25 end

  為了方便測試,設計樣式生成模塊與擴展接口模塊用戶側連接,不斷向一段地址寫入固定數據序列並在一段時間后讀回。

  1 `timescale 1ns / 1ps
  2 /*
  3 該模塊功能:
  4 周期性向一段地址執行讀寫操作 產生固定樣式待寫入數據用戶測試目的
  5 測試完畢后刪除該模塊,開發用戶接口
  6 
  7 具體為:
  8 1 寫從0開始之后的10個用戶地址(80個DDR地址):0~9遞增序列
  9 2 等待20個時鍾周期
 10 3 讀取寫入的10個用戶地址
 11 4 等待20個時鍾周期
 12 5 重復上述步驟
 13 
 14 說明:
 15 1 每個步驟之間有一個時鍾周期空閑
 16 2 由於burst_len = 8 4:1時鍾模式下一個用戶時鍾周期寫入數據對應同樣時間內8個DDR時鍾邊沿寫入數據,
 17 因此地址遞增步長為8
 18 */
 19 module traffic_gen
 20 #(parameter DATA_WIDTH = 32,
 21             ADDR_WIDTH = 29)
 22 (
 23     input                           clk   ,
 24     input                           rst_n ,
 25 
 26     output reg                      gen_wdata_en ,
 27     output reg [ ADDR_WIDTH-1:0]    gen_waddr    ,
 28     output reg [ DATA_WIDTH-1:0]    gen_wdata ,
 29     input                           gen_wdata_rdy ,//寫指令和數據通道准備就緒
 30 
 31     output reg                      gen_rdata_en ,
 32     output reg [ ADDR_WIDTH-1:0]    gen_raddr    ,
 33     input      [ DATA_WIDTH-1:0]    gen_rdata     ,
 34     input                           gen_rdata_vld ,
 35     input                           gen_rdata_rdy //讀指令通道准備就緒
 36 );
 37 
 38    
 39 reg [ (8-1):0]  cnt0     ;
 40 wire        add_cnt0 ;
 41 wire        end_cnt0 ;
 42 reg [ (2-1):0]  cnt1     ;
 43 wire        add_cnt1 ;
 44 wire        end_cnt1 ;
 45 
 46 reg [ DATA_WIDTH-1:0]  gen_rdata_r     ;
 47 reg   gen_rdata_vld_r     ;
 48 reg    com_flag     ;
 49 
 50 wire wri_state;
 51 wire rd_state;
 52 wire com_change_t;
 53 
 54 //操作周期計數器,計數值為欲操作用戶地址段長度+1(需要一個時鍾周期空閑)
 55 always @(posedge clk or negedge rst_n) begin 
 56     if (rst_n==0) begin
 57         cnt0 <= 0; 
 58     end
 59     else if(add_cnt0) begin
 60         if(end_cnt0)
 61             cnt0 <= 0; 
 62         else
 63             cnt0 <= cnt0+1 ;
 64    end
 65 end
 66 assign add_cnt0 = (com_flag == 0 && gen_wdata_rdy) || (com_flag == 1 && gen_rdata_rdy);
 67 assign end_cnt0 = add_cnt0  && cnt0 == (30)-1 ;
 68 
 69 //指令標志位 先是0--寫 再是1--讀
 70 always @(posedge clk or negedge rst_n )begin 
 71     if(rst_n==0) begin
 72         com_flag <= (0)  ;
 73     end
 74     else if(com_change_t)begin
 75         com_flag <= (!com_flag)  ;
 76     end 
 77 end
 78 
 79 assign com_change_t = add_cnt0 && cnt0 == 10 - 1;
 80 
 81 //寫操作---------------------------------------------
 82 always @(posedge clk or negedge rst_n )begin 
 83     if(rst_n==0) begin
 84         gen_wdata_en <= (0)  ;
 85     end
 86     else if(wri_state)begin
 87         gen_wdata_en <= (1'b1)  ;
 88     end 
 89     else begin
 90         gen_wdata_en <= (0)  ;
 91     end 
 92 end
 93 
 94 assign wri_state = add_cnt0 && cnt0 <= 10-1 && com_flag == 0;
 95 assign rd_state  = add_cnt0 && cnt0 <= 10-1 && com_flag == 1;
 96 
 97 always @(posedge clk or negedge rst_n )begin 
 98     if(rst_n==0) begin
 99         gen_wdata <= (0)  ;
100     end
101     else begin
102         gen_wdata <= (cnt0)  ;
103     end 
104 end
105 
106 always@(posedge clk or negedge rst_n)begin
107     if(rst_n == 0)
108         gen_waddr <= 0;
109     else if(wri_state)
110         gen_waddr <= gen_waddr + 29'd8;
111     else 
112         gen_waddr <= 0;
113 end
114 //讀操作----------------------------------------------
115 
116 always @(posedge clk or negedge rst_n )begin 
117     if(rst_n==0) begin
118         gen_rdata_en <= (0)  ;
119     end
120     else if(rd_state)begin
121         gen_rdata_en <= (1'b1)  ;
122     end 
123     else begin
124         gen_rdata_en <= (0)  ;
125     end 
126 end
127 
128 always@(posedge clk or negedge rst_n)begin
129     if(rst_n == 0)
130         gen_raddr <= 0;
131     else if(rd_state)
132         gen_raddr <= gen_raddr + 29'd8;
133     else 
134         gen_raddr <= 0;
135 end
136 
137 always @(posedge clk or negedge rst_n )begin 
138     if(rst_n==0) begin
139         gen_rdata_r <= (0)  ;
140     end
141     else begin
142         gen_rdata_r <= (gen_rdata)  ;
143     end 
144 end
145 
146 always @(posedge clk or negedge rst_n )begin 
147     if(rst_n==0) begin
148         gen_rdata_vld_r <= (0)  ;
149     end
150     else if(gen_rdata_vld)begin
151         gen_rdata_vld_r <= (1'b1)  ;
152     end 
153     else begin
154         gen_rdata_vld_r <= (0)  ;
155     end 
156 end
157 
158 endmodule

   將traffic_gen和extend_interface模塊例化在MIG的example design中,利用ILA抓取MIG IP核用戶接口信號。

 

 

  向地址8~80寫入數據0~9,再從此段地址中讀回數據,0~9被正確讀出,MIG IP核控制DDR3讀寫測試完畢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM