上一节已经实现了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
知识兔View Code仿真结果:
特备注意:关于这里的存储地址字节以及用户数据突发长度之间的关系要自行理解清楚