回看《例说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图: