回看《例說FPGA》
---DDR2控制器集成與讀寫測試
1.DDR2 IP核的配置
需要弄清楚的選項主要有:
PLL reference clock frequency
Memory clock frequency
Controller data rate
對於DDR2 芯片的選型,可以在Memory Presets 里面選擇,如果沒有符合的器件,可以任意選中一個器件,點擊modify parameters 按鈕,在 preset Editor 里面進行參數配置。
2.片上RAM的配置
需要設置的選項有:
輸出數據的位寬
存儲器的深度
雙時鍾或單時鍾
雙時鍾意味着輸入輸出采用不同的時鍾:
在regs/clken/byte enable/aclrs選項卡中,需要設置的是:
Which ports should be registered? ‘ q output port’,如果選中,則在輸出端加上一個觸發器,輸出數據時候,就會用clock信號打節拍。如果不選中,則:
3.數據源產生模塊
實驗的整體框圖如下圖所示:
數據源產生模塊實現的功能為:
每秒定時DDR2數據寫入控制;
每秒定時DDR2數據讀出控制;
將讀出的數據寫入片上RAM中(RAM IP核在此模塊中進行實例化);
此模塊主要用來與DDR2 controller IP核進行通信,首先需要對DDR2 controller的信號多一些了解:
1 module ddr2_contral ( 2 local_address, 3 local_write_req, 4 local_read_req, 5 local_burstbegin, 6 local_wdata, 7 local_be, 8 local_size, 9 global_reset_n, 10 pll_ref_clk, 11 soft_reset_n, 12 local_ready, 13 local_rdata, 14 local_rdata_valid, 15 local_refresh_ack, 16 local_init_done, 17 reset_phy_clk_n, 18 mem_odt, 19 mem_cs_n, 20 mem_cke, 21 mem_addr, 22 mem_ba, 23 mem_ras_n, 24 mem_cas_n, 25 mem_we_n, 26 mem_dm, 27 phy_clk, 28 aux_full_rate_clk, 29 aux_half_rate_clk, 30 reset_request_n, 31 mem_clk, 32 mem_clk_n, 33 mem_dq, 34 mem_dqs); 35 36 37 input [22:0] local_address; 38 input local_write_req; 39 input local_read_req; 40 input local_burstbegin; 41 input [63:0] local_wdata; 42 input [7:0] local_be; 43 input [2:0] local_size; 44 input global_reset_n; 45 input pll_ref_clk; 46 input soft_reset_n; 47 output local_ready; 48 output [63:0] local_rdata; 49 output local_rdata_valid; 50 output local_refresh_ack; 51 output local_init_done; 52 output reset_phy_clk_n; 53 output [0:0] mem_odt; 54 output [0:0] mem_cs_n; 55 output [0:0] mem_cke; 56 output [12:0] mem_addr; 57 output [1:0] mem_ba; 58 output mem_ras_n; 59 output mem_cas_n; 60 output mem_we_n; 61 output [1:0] mem_dm; 62 output phy_clk; 63 output aux_full_rate_clk; 64 output aux_half_rate_clk; 65 output reset_request_n; 66 inout [0:0] mem_clk; 67 inout [0:0] mem_clk_n; 68 inout [15:0] mem_dq; 69 inout [1:0] mem_dqs;
ddr2 controller模塊的接口可以分為三類:
第一類:系統接口,包含系統或PLL的復位,時鍾等接口;
第二類:以local開頭的接口,是DDR2 IP核與用戶邏輯間的接口;(數據源產生模塊主要用到的就是這類接口信號,需要弄懂這些信號的操作時序)
第三類:以mem開頭的接口,是DDR2 IP核與FPGA外部DDR2芯片的接口;(只需要做引腳鎖定,編程基本用不到)
關於使用DDR2 IP核用戶外部邏輯操作loca信號的時序圖如下:
在遇到local_ready拉低的讀操作,下列信號必須保持到local_ready拉高為止:
local_read_req;
local_size;
local_addr;
寫數據的時候,整個過程中,必須保持local_write_req信號一直有效。
邏輯代碼:
1 module data_source( 2 clk, //75MHz 3 rst_n, 4 local_address, 5 local_write_req, 6 local_read_req, 7 local_wdata, 8 local_ready, 9 local_rdata, 10 local_rdata_valid, 11 local_init_done 12 ); 13 14 // 1/75MHz = 13.33ns; 15 input clk; 16 input rst_n; 17 output [22:0]local_address; 18 output local_write_req; 19 output local_read_req; 20 output [63:0]local_wdata; 21 input local_ready; 22 input [63:0]local_rdata; 23 input local_rdata_valid; 24 input local_init_done; 25 26 reg[26:0] scnt; 27 reg[7:0] times; 28 29 //計數器,當local_init_done有效時,計數器scnt開始計數 30 always @(posedge clk or negedge rst_n) 31 if(!rst_n) scnt <= 27'd0; 32 else if(local_init_done) scnt <= scnt+1'b1; 33 34 wire timer_wrreq = (scnt == 27'h00_001_000); // 55us 35 wire timer_rdreq = (scnt == 27'h00_005_000); // 273us 36 37 //每當timer_rdreq有效時,計數器times增加1,此模塊用來計數讀取數據的個數 38 always @(posedge clk or negedge rst_n) 39 if(!rst_n) times <= 8'd0; 40 else if(timer_rdreq) times <= times+1'b1; 41 42 //狀態機 43 parameter SIDLE = 4'd0; 44 parameter SWRDB = 4'd1; 45 parameter SRDDB = 4'd2; 46 parameter SSTOP = 4'd3; 47 48 reg[3:0] cstate; 49 reg[8:0] num; 50 51 always @(posedge clk or negedge rst_n) 52 if(!rst_n) cstate <= SIDLE; 53 else begin 54 case(cstate) 55 SIDLE: begin //控制狀態機的跳轉,timer_wrreq有效,則跳轉到SWRDB,timer_rdreq有效,則跳轉到SRDDB 56 if(timer_wrreq) cstate <= SWRDB; 57 else if(timer_rdreq) cstate <= SRDDB; 58 else cstate <= SIDLE; 59 end 60 SWRDB: begin //如果寫入255個數據,並且local_ready有效,則狀態結束 61 if((num == 9'd255) && local_ready) cstate <= SSTOP; 62 else cstate <= SWRDB; 63 end 64 SRDDB: begin ////如果讀取255個數據,並且local_ready有效,則狀態結束 65 if((num == 9'd255) && local_ready) cstate <= SSTOP; 66 else cstate <= SRDDB; 67 end 68 SSTOP: cstate <= SIDLE; 69 default: cstate <= SIDLE; 70 endcase 71 end 72 73 always @(posedge clk or negedge rst_n) 74 if(!rst_n) num <= 9'd0; 75 else if((cstate == SWRDB) || (cstate == SRDDB)) begin //如果狀態處在SWRDB或者是SRDDB時,進入下一個判斷階段 76 if(local_ready) num <= num+1'b1; //如果local_ready有效時,num開始計數 77 else ; 78 end 79 else num <= 9'd0; 80 81 assign local_address = (cstate == SWRDB) ? {13'h0a55,2'd1,num[7:0]}:{13'h0a55,2'd1,num[7:0]}; //地址采用num這個寄存器來進行地址偏移 82 assign local_wdata = {times,{num[5:0],2'b00},times,{num[5:0],2'b01},times,{num[5:0],2'b10},times,{num[5:0],2'b11}}; 83 assign local_write_req = (cstate == SWRDB); //當狀態機處在SWRDB時,發出寫請求 84 assign local_read_req = (cstate == SRDDB); //當狀態機處在SRDDB時,發出讀請求 85 86 reg[7:0] ram_addr; 87 always @(posedge clk or negedge rst_n) 88 if(!rst_n) ram_addr <= 8'd0; 89 else if(timer_rdreq) ram_addr <= 8'd0; //當timer_rdreq讀數請求有效時,ram_addr清零,即讀數據請求到來時,ram_addr指向第一個地址 90 else if(local_rdata_valid) ram_addr <= ram_addr+1'b1; //當local_rdata_valid信號有效時候,片上ram_addr開始偏移 91 else ; 92 93 //此ram只寫入,不輸出 94 ram_1port u1 ( 95 .address(ram_addr), 96 .clock(clk), 97 .data(local_rdata), 98 .wren(local_rdata_valid), 99 .q() 100 ); 101 102 endmodule
綜合結果,RTL圖: