上一节已经实现了DDR3的写数据的驱动、命令端口、写数据端口的介绍以及DDR3的用户数据长度、突发字节等相关寄存器的配置,最终成功地实现了向DDR3中写入一个0-15的连续递增的数据。这一节,就在上一节的基础上继续实现DDR3的读时序及其仿真。
DDR3读数据的时序:
用户界面的读取路径使用简单的64深度FIFO结构来保存从Read事务返回的数据。Read DataFIFO中的空标志(pX_rd_empty)可用作数据有效指示符。每当pX_rd_empty置为无效时,pX_rd_data总线上就会出现无效数据。要将数据从读数据FIFO传输到FPGA逻辑,必须在pX_rd_clk的上升沿置位pX_rd_en信号.pX_rd_data总线在pX_rd_clk的上升沿跳变。pX_rd_en信号可以如果需要,pX_rd_empty信号可以始终保持断言,并且可以用作数据有效指示符。
架构和框图:
注意:在Rd_en 有效之前,先保证READ DATA FIFO中有一定的数据,可以先执行读指令,P1端口专门用来,从DDR3芯片中读取数据
时序设计:
注意:我们在写入数据,后读数据是这两个时间段之间,要留有一定的时间间隔。防止发生冲突。在此实验中我留出的时间间隔为10个时钟。相关介绍和思路说明:
p0_cmd_en ;是上一节我们讲的写入时产生的命令。
rd_flag : 是表示当前读数据的工作标志,高电平代表正在进行读数据。
p1_cmd_en:读数据端口的相关命令使能,在p0_cmd_en拉高后,也被拉高,当此信号被拉高是可以向DDR 写入相关寄存器的配置命令。即把p1端口配置成读数据的端口和状态。
rd_cnt :该信号表示延迟以及写入的数据个数。
p1_rd_en:在该信号为高是,开始读取DDR中的数据。
相关代码以及逻辑实现:
(1)端口设置:
1 //ddr interface (read part) 2 output wire p1_rd_en , 3 output reg p1_cmd_en , 4 output wire [5:0] p1_cmd_bl , 5 output wire [2:0] p1_cmd_instr , 6 output reg [29:0] p1_cmd_addr , 7 output wire [63:0] p1_rd_data ,
(2)中间变量:
1 localparam RD_END = 'd25 ;//计数结束终端 2 reg rd_flag ;//读工作状态标志 3 reg [4:0] rd_cnt ;
(3)相关时序配置:
1 /******************************************************* 2 ***************read part ******************************* 3 *********************************************************/ 4 //rd_flag 5 always @(posedge sclk or negedge s_rst_n)begin 6 if(!s_rst_n) 7 rd_flag <= 1'b0 ; 8 else if(p0_cmd_en == 1'b1 ) 9 rd_flag <= 1'b1 ; 10 else if(rd_cnt == 5'd25) 11 rd_flag <= 1'b0 ; 12 13 end 14 //p1_rd_en 15 always @(posedge sclk or negedge s_rst_n)begin 16 if(!s_rst_n) 17 p1_cmd_en <= 1'b0 ; 18 else if(p0_cmd_en==1'b1) 19 p1_cmd_en <= 1'b1 ; 20 else if(rd_cnt == 1'b1 ) 21 p1_cmd_en <= 1'b0 ; 22 23 end 24 //rd_cnt 25 always @(posedge sclk or negedge s_rst_n)begin 26 if(!s_rst_n) 27 rd_cnt <= 5'd0; 28 else if(rd_flag == 1'b1 ) 29 rd_cnt <= rd_cnt + 1'b1 ; 30 else if(rd_flag == 1'b0 ) 31 rd_cnt <= 5'd0 ; 32 end 33 34 //p1_cmd_addr 35 always @(posedge sclk or negedge s_rst_n)begin 36 if(!s_rst_n) 37 p1_cmd_addr <= 'd0 ; 38 else if(p1_cmd_en==1'b1 ) 39 p1_cmd_addr <= p1_cmd_addr+ 'd64; 40 end 41
(4)相关寄存器以及命令
1 //读数据端口 2 assign p1_cmd_bl = 'd7 ; 3 assign p1_cmd_instr = 3'b001 ; 4 assign p1_rd_en = (rd_cnt>=10 && rd_cnt <= RD_END) ? 1'b1:1'b0;
TB测试文件:因为上一节我们已经成功地写入了数据,所以这次在上次的基础上,进行仿真和测试。
写入数据和读出数据的总代码:
1 module ddr_drive( 2 3 //systerm signals 4 input wire sclk , 5 input wire s_rst_n , 6 //ddr interface (write part) 7 output reg p0_wr_en , 8 output wire p0_cmd_en , 9 output wire [5:0] p0_cmd_bl , 10 output wire [2:0] p0_cmd_instr , 11 output wire [29:0]p0_cmd_addr , 12 output reg [63:0]p0_wr_data , 13 output wire [7:0] p0_wr_mask , 14 //ddr interface (read part) 15 output wire p1_rd_en , 16 output reg p1_cmd_en , 17 output wire [5:0] p1_cmd_bl , 18 output wire [2:0] p1_cmd_instr , 19 output reg [29:0] p1_cmd_addr , 20 output wire [63:0] p1_rd_data , 21 //debug signals 22 input wire wr_trig 23 24 ); 25 /********************************************************************** 26 ****************define parameter and signals************************** 27 ***********************************************************************/ 28 localparam RD_END = 'd25 ;//计数结束终端 29 30 reg wr_en_neg ;//negedge flag 31 32 reg rd_flag ;//读工作状态标志 33 reg [4:0] rd_cnt ; 34 /********************************************************************** 35 *******************main code ***************************************** 36 ***********************************************************************/ 37 //写数据端口 38 assign p0_cmd_bl = 'd15 ; 39 assign p0_cmd_instr = 3'b000 ; 40 41 assign p0_cmd_en = ~p0_wr_en & wr_en_neg ; 42 assign p0_cmd_addr = 'd0 ; 43 assign p0_wr_mask = 8'h0 ; 44 45 //读数据端口 46 assign p1_cmd_bl = 'd7 ; 47 assign p1_cmd_instr = 3'b001 ; 48 assign p1_rd_en = (rd_cnt>=10 && rd_cnt <= RD_END) ? 1'b1:1'b0; 49 //assign p1_cmd_addr = 'd0 ; 50 //p0_wr_en 51 always @(posedge sclk or negedge s_rst_n)begin 52 if(!s_rst_n) 53 p0_wr_en <= 1'b0 ; 54 else if(p0_wr_data>=15) 55 p0_wr_en <= 1'b0 ; 56 else if (wr_trig == 1'b1) 57 p0_wr_en <= 1'b1 ; 58 end 59 60 //p0_wr_data 61 always @(posedge sclk or negedge s_rst_n)begin 62 if(!s_rst_n) 63 p0_wr_data <= 'd0 ; 64 else if(p0_wr_en == 1'b1 ) 65 p0_wr_data <= p0_wr_data + 1'b1 ; 66 67 end 68 69 70 //wr_en_neg.边沿检测 71 always @(posedge sclk )begin 72 wr_en_neg <= p0_wr_en ; 73 74 end 75 76 /******************************************************* 77 ***************read part ******************************* 78 *********************************************************/ 79 //rd_flag 80 always @(posedge sclk or negedge s_rst_n)begin 81 if(!s_rst_n) 82 rd_flag <= 1'b0 ; 83 else if(p0_cmd_en == 1'b1 ) 84 rd_flag <= 1'b1 ; 85 else if(rd_cnt == 5'd25) 86 rd_flag <= 1'b0 ; 87 88 end 89 //p1_rd_en 90 always @(posedge sclk or negedge s_rst_n)begin 91 if(!s_rst_n) 92 p1_cmd_en <= 1'b0 ; 93 else if(p0_cmd_en==1'b1) 94 p1_cmd_en <= 1'b1 ; 95 else if(rd_cnt == 1'b1 ) 96 p1_cmd_en <= 1'b0 ; 97 98 end 99 //rd_cnt 100 always @(posedge sclk or negedge s_rst_n)begin 101 if(!s_rst_n) 102 rd_cnt <= 5'd0; 103 else if(rd_flag == 1'b1 ) 104 rd_cnt <= rd_cnt + 1'b1 ; 105 else if(rd_flag == 1'b0 ) 106 rd_cnt <= 5'd0 ; 107 end 108 109 //p1_cmd_addr 110 always @(posedge sclk or negedge s_rst_n)begin 111 if(!s_rst_n) 112 p1_cmd_addr <= 'd0 ; 113 else if(p1_cmd_en==1'b1 ) 114 p1_cmd_addr <= p1_cmd_addr+ 'd64; 115 end 116 117 118 endmodule
顶层的代码变化以及例化的变化:
1 module ddr_top( 2 3 4 5 //sysyterm interface 6 input c3_sys_clk , 7 input c3_sys_rst_i , 8 //ddr3 interface 9 inout [15:0] mcb3_dram_dq , 10 output wire [12:0] mcb3_dram_a , 11 output wire [2:0] mcb3_dram_ba , 12 output wire mcb3_dram_ras_n , 13 output wire mcb3_dram_cas_n , 14 output wire mcb3_dram_we_n , 15 output wire mcb3_dram_odt , 16 output wire mcb3_dram_reset_n , 17 output wire mcb3_dram_cke , 18 output wire mcb3_dram_dm , 19 inout mcb3_dram_udqs , 20 inout mcb3_dram_udqs_n , 21 inout mcb3_rzq , 22 inout mcb3_zio , 23 output wire mcb3_dram_udm , 24 inout mcb3_dram_dqs , 25 inout mcb3_dram_dqs_n , 26 output wire mcb3_dram_ck , 27 output wire mcb3_dram_ck_n , 28 //debug 29 input wire wr_trig , 30 input wire c3_calib_done 31 32 ); 33 34 /********************************************************************* 35 *************************signals define******************************** 36 **********************************************************************/ 37 38 39 40 //ddr interface (write modle ) 41 wire p0_wr_en ; 42 wire p0_cmd_en ; 43 wire [5:0] p0_cmd_bl ; 44 wire [2:0] p0_cmd_instr ; 45 wire [29:0]p0_cmd_addr ; 46 wire [63:0]p0_wr_data ; 47 wire [7:0] p0_wr_mask ; 48 49 //ddr interface (read part) 50 wire p1_rd_en ; 51 wire p1_cmd_en ; 52 wire [5:0] p1_cmd_bl ; 53 wire [2:0] p1_cmd_instr ; 54 wire [29:0] p1_cmd_addr ; 55 wire [63:0] p1_rd_data ; 56 57 58 59 60 61 /********************************************************************* 62 ****************************main code ******************************** 63 **********************************************************************/ 64 ddr_drive ddr_drive_inst( 65 66 //systerm signals 67 .sclk (c3_clk0 ), 68 .s_rst_n (~c3_rst0 ), 69 //ddr interface 70 .p0_wr_en (p0_wr_en ), 71 .p0_cmd_en (p0_cmd_en ), 72 .p0_cmd_bl (p0_cmd_bl ), 73 .p0_cmd_instr (p0_cmd_instr), 74 .p0_cmd_addr (p0_cmd_addr ), 75 .p0_wr_data (p0_wr_data ), 76 .p0_wr_mask (p0_wr_mask ), 77 //ddr interface (read part) 78 .p1_rd_en (p1_rd_en ), 79 .p1_cmd_en (p1_cmd_en ), 80 .p1_cmd_bl (p1_cmd_bl ), 81 .p1_cmd_instr (p1_cmd_instr ), 82 .p1_cmd_addr (p1_cmd_addr ), 83 .p1_rd_data (p1_rd_data ), 84 //debug signals 85 .wr_trig (wr_trig ) 86 87 ); 88 89 90 91 92 93 mig_39_2 # ( 94 .C3_P0_MASK_SIZE(8), 95 .C3_P0_DATA_PORT_SIZE(64), 96 .C3_P1_MASK_SIZE(8), 97 .C3_P1_DATA_PORT_SIZE(64), 98 .DEBUG_EN(0), 99 .C3_MEMCLK_PERIOD(3200),//当前的时钟周期 100 .C3_CALIB_SOFT_IP("TRUE"), 101 .C3_SIMULATION("TRUE"),//仿真 102 .C3_RST_ACT_LOW(1),//复位信号的配置 103 .C3_INPUT_CLK_TYPE("SINGLE_ENDED"),//时钟模式 104 .C3_MEM_ADDR_ORDER("BANK_ROW_COLUMN"),//内存读取的顺序模式 105 .C3_NUM_DQ_PINS(16), 106 .C3_MEM_ADDR_WIDTH(13), 107 .C3_MEM_BANKADDR_WIDTH(3) 108 ) 109 u_mig_39_2 ( 110 //DDR3 的接口 111 .c3_sys_clk (c3_sys_clk), //input DDR3的参考时钟 112 .c3_sys_rst_i (c3_sys_rst_i), //input DDR3的复位信号 113 114 .mcb3_dram_dq (mcb3_dram_dq), 115 .mcb3_dram_a (mcb3_dram_a), 116 .mcb3_dram_ba (mcb3_dram_ba), 117 .mcb3_dram_ras_n (mcb3_dram_ras_n), 118 .mcb3_dram_cas_n (mcb3_dram_cas_n), 119 .mcb3_dram_we_n (mcb3_dram_we_n), 120 .mcb3_dram_odt (mcb3_dram_odt), 121 .mcb3_dram_cke (mcb3_dram_cke), 122 .mcb3_dram_ck (mcb3_dram_ck), 123 .mcb3_dram_ck_n (mcb3_dram_ck_n), 124 .mcb3_dram_dqs (mcb3_dram_dqs), 125 .mcb3_dram_dqs_n (mcb3_dram_dqs_n), 126 .mcb3_dram_udqs (mcb3_dram_udqs), // for X16 parts 127 .mcb3_dram_udqs_n (mcb3_dram_udqs_n), // for X16 parts 128 .mcb3_dram_udm (mcb3_dram_udm), // for X16 parts 129 .mcb3_dram_dm (mcb3_dram_dm), 130 .mcb3_dram_reset_n (mcb3_dram_reset_n), 131 132 133 134 135 //sppourt for user 136 .c3_clk0 (c3_clk0),//output 输出给用户提供的 137 .c3_rst0 (c3_rst0),//output 输出给用户提供的 138 139 140 141 .c3_calib_done (c3_calib_done), 142 .mcb3_rzq ( mcb3_rzq ), 143 .mcb3_zio (mcb3_zio ), 144 145 //P0,p1表示两个用户会接口 146 /*********************command path****************************/ 147 .c3_p0_cmd_clk (c3_clk0 ), //命令FIFO的用户时钟。 FIFO信号是 在这个时钟的上升沿捕获。 148 .c3_p0_cmd_en (p0_cmd_en), //该高电平有效信号是用于写入的写入使能信号命令FIFO。 149 .c3_p0_cmd_instr (p0_cmd_instr), //当前指令的命令代码。 位0表示READ / WRITE选择,Bit 1为Auto预充电启用,位2代表刷新总是优先考虑 150 .c3_p0_cmd_bl (p0_cmd_bl), //当前用户字数的突发长度交易。 突发长度编码为0到63,代表1到64个用户词(例如,6'b00011 是一个突发长度4的交易)。 用户字宽等于端口宽度(例如,突发长度为3 64位端口传输3 x 64位用户字= 192位 总)。 151 /*当前事务的字节起始地址。地址 152 必须与端口大小对齐: 153 32位端口:低两位必须为0。 154 64位端口:低三位必须为0。 155 128位端口:低4位必须为0*/ 156 .c3_p0_cmd_byte_addr (p0_cmd_addr), 157 .c3_p0_cmd_empty ( ), //这个命令FIFO的高电平有效空标志 158 .c3_p0_cmd_full ( ), //此高电平有效输出是命令的man标志 159 160 /*********************write cmd****************************/ 161 .c3_p0_wr_clk (c3_clk0 ),//该信号是写数据FIFO的用户时钟 162 /*该高电平有效信号是写使能 163 用于写数据FIFO。它表明了 164 pX_wr_data上的值有效 165 加载到FIFO。数据已加载 166 pX_wr_clk的上升沿时 167 pX_wr_en = 1且pX_wr_full = 0。*/ 168 .c3_p0_wr_en (p0_wr_en), 169 .c3_p0_wr_mask (p0_wr_mask),//写数据的掩码, 170 /*写入要写入的数据值 171 数据FIFO并发送到内存。 PX_SIZE 172 可以是32位,64位或128位,具体取决于 173 端口配置*/ 174 .c3_p0_wr_data (p0_wr_data), 175 .c3_p0_wr_full ( ), //高有效的满信号 176 .c3_p0_wr_empty ( ),//高有效的空信号 177 /*写入数据FIFO的计数值。这个 178 输出表示有多少用户单词 179 在FIFO中(从1到64)。计数值为 180 0表示FIFO为空。这个信号 181 延迟的延迟比 182 pX_wr_empty标志。因此,FIFO 183 可能是空的或经历不足 184 即使计数不为0。*/ 185 .c3_p0_wr_count ( ), 186 .c3_p0_wr_underrun ( ),//高电平有效,欠载标志。 187 .c3_p0_wr_error ( ), 188 /*********************read cmd****************************/ 189 .c3_p0_rd_clk (0 ),//该信号是du数据FIFO的用户时钟 190 .c3_p0_rd_en (0 ), 191 .c3_p0_rd_data ( ), 192 .c3_p0_rd_full ( ), 193 .c3_p0_rd_empty ( ), 194 .c3_p0_rd_count ( ), 195 .c3_p0_rd_overflow ( ), 196 .c3_p0_rd_error ( ), 197 /********************P1 user port************************/ 198 .c3_p1_cmd_clk (c3_clk0 ), 199 .c3_p1_cmd_en (p1_cmd_en ), 200 .c3_p1_cmd_instr (p1_cmd_instr ), 201 .c3_p1_cmd_bl (p1_cmd_bl ), 202 .c3_p1_cmd_byte_addr (p1_cmd_addr ), 203 .c3_p1_cmd_empty ( ), 204 .c3_p1_cmd_full ( ), 205 206 .c3_p1_wr_clk ( 0 ), 207 .c3_p1_wr_en ( 0 ), 208 .c3_p1_wr_mask ( 0 ), 209 .c3_p1_wr_data ( 64'h0 ), 210 .c3_p1_wr_full ( ), 211 .c3_p1_wr_empty ( ), 212 .c3_p1_wr_count ( ), 213 .c3_p1_wr_underrun ( ), 214 .c3_p1_wr_error ( ), 215 //读数据用到的端口 216 .c3_p1_rd_clk ( c3_clk0 ), 217 .c3_p1_rd_en ( p1_rd_en ), 218 .c3_p1_rd_data ( p1_rd_data ), 219 .c3_p1_rd_full ( ), 220 .c3_p1_rd_empty ( ), 221 .c3_p1_rd_count ( ), 222 .c3_p1_rd_overflow ( ), 223 .c3_p1_rd_error ( ) 224 ); 225 226 227 endmodule
仿真结果:
特备注意:关于这里的存储地址字节以及用户数据突发长度之间的关系要自行理解清楚