上一节已经成功地字节仿照DDR的官方例子,写出了自己的驱动,并且谢了下关的激励文件,接下来就主要介绍怎么样实现DDR的写数据操作,以及相关端口的介绍,首先根据我们的例子以及我们上一节自己写的仿真,对相关端口介绍一下。
DDR IP中最核心的代码就是u_mig_39_2中的端口例化(这个名称是你在创建DDR IP时自己默认的,当然依然可以修改),其中的接口可以分为俩大部分,可以总结为以下的结构框图,方便理解和记忆(VISIO画图的时候字体选的6PT,有点模糊哈)。
其中以下几个信号是提供给用户使用的,用户可以用这两个端口来设置DDR IP的参考时钟和复位信号,都是输入类型的信号(针对DDR来说)
c3_sys_clk :input DDR3的参考时钟
c3_sys_rst_i :input DDR3的复位信号
c3_calib_done :当DDR的所有初始化信息完成后此信号拉高,才能对DDR惊醒相关的操作
mcb3_rzq :硬件接电阻的端口
mcb3_zio:硬件接电阻的端口
命令路径端口及作用:
c3_p0_cmd_clk //命令FIFO的用户时钟。 FIFO信号是 在这个时钟的上升沿捕获
c3_p0_cmd_en //该高电平有效信号是用于写入的写入使能信号命令FIFO。
c3_p0_cmd_instr //当前指令的命令代码。 位0表示READ / WRITE选择,Bit 1为Auto预充电启用,位2代表刷新总是优先考虑
Write: 3'b000 Read: 3'b001 Write with Auto Precharge: 3'b010 Read with Auto Precharge: 3'b011 Refresh: 3'b1xx |
c3_p0_cmd_bl//当前用户字数的突发长度交易。 突发长度编码为0到63,代表1到64个用户词(例如,6'b00011 是一个突发长度4的交易)。 用户字宽等于端口宽度(例如,突发长度为3 64位端口传输3 x 64位用户字= 192位 总)。
c3_p0_cmd_byte_addr/*当前事务的字节起始地址。地址必须与端口大小对齐: 32位端口:低两位必须为0。 64位端口:低三位必须为0。 128位端口:低4位必须为0*/
c3_p0_cmd_empty /这个命令FIFO的高电平有效空标志
c3_p0_cmd_full/此高电平有效输出是命令的man标志l
路径命令的时序:
用户界面的命令路径使用简单的4深度FIFO结构来保存待定命令。 请求的指令类型,地址和突发长度事务全部加载到此命令FIFO中。 来自的完整标志(pX_cmd_full)信号命令FIFO必须为低电平才能将新命令接收到FIFO中在pX_cmd_clk的上升沿期间,pX_cmd_en被置位。 否则,命令是忽略。 图4-4和图4-5说明了加载命令的协议FIFO。
写命令的端口及其作用:
c3_p0_wr_clk ://该信号是写数据FIFO的用户时钟
c3_p0_wr_en ://写数据使能,高有效,拉高的时候才能写入数据。8
c3_p0_wr_mask://写数据的掩码, 8BIT大小,适合一个数据的字节关联的,决定了当前字节是否可以写入到MEMERY当中
c3_p0_wr_data :写入要写入的数据值 数据FIFO并发送到内存。 PX_SIZE 可以是32位,64位或128位,具体取决于 端口配置
c3_p0_wr_full ://高有效的满信号
c3_p0_wr_empty:/高有效的空信号
c3_p0_wr_count ://用来表示当前写FIFO内部含有多少个数据
c3_p0_wr_underrun://
c3_p0_wr_error://可以当做一个状态输出
写命令的时序:
用户界面的写入路径使用简单的64深度FIFO结构来保存数据准备将事务写入内存。与命令FIFO类似,满标志写入数据FIFO中的(pX_wr_full)必须为低电平才能接受新数据在pX_wr_clk的上升沿期间pX_wr_en被置位时的FIFO。否则,数据被忽略了。如果满标志为低,则pX_wr_data总线在上升时被捕获到FIFO中,对于每一个PX_WR_EN有效的时候,PX_WR_DATA数据总线上必须有有效的数据。
命令路径和写路径之间的协调:
命令路径可以看成是一个命令 FIFO;写数据路径可以看成是一个写数据 FIFO。
在执行写命令之前要确保Write data FIFO中是有数据的。方法:先向Write data FIFO中写入想要写入到DDR内部的数据,之后再向CMD FIFO中写入相应的命令。以确保在执行写命令之前,我的write data FIFO中已有数据。
可以得出以下时序图:
其中Wr_trig是调试信号,
接下来就是写代码了:
(1)端口:包括写命令路径和写数据路径,还包括时钟、复位等信号。
1 module ddr_drive( 2 3 //systerm signals 4 input wire sclk , 5 input wire s_rst_n , 6 //ddr interface 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 //debug signals 15 input wire wr_trig 16 17 );
(2)产生写使能信号:
1 //p0_wr_en 2 always @(posedge sclk or negedge s_rst_n)begin 3 if(!s_rst_n) 4 p0_wr_en <= 1'b0 ; 5 else if(p0_wr_data>=15) 6 p0_wr_en <= 1'b0 ; 7 else if (wr_trig == 1'b1) 8 p0_wr_en <= 1'b1 ; 9 end
(3)产生要写入的数据
1 //p0_wr_data 2 always @(posedge sclk or negedge s_rst_n)begin 3 if(!s_rst_n) 4 p0_wr_data <= 'd0 ; 5 else if(p0_wr_en == 1'b1 ) 6 p0_wr_data <= p0_wr_data + 1'b1 ; 7 8 end
(4)配置写入的命令,就是配置相关DDR的突发长度等等
1 assign p0_cmd_bl = 'd16 ; 2 assign p0_cmd_instr = 3'b000 ; 3 4 assign p0_cmd_en = ~p0_wr_en & wr_en_neg ; 5 assign p0_cmd_addr = 'd0 ; 6 assign p0_wr_mask = 8'h0 ;
(5)中间变量
1 reg wr_en_neg ;//negedge flag 2 //wr_en_neg.边沿检测 3 always @(posedge sclk )begin 4 wr_en_neg <= p0_wr_en ; 5 6 end 7
驱动和写入一个0-15的数据的小驱动就完成了
(6)TB激励文件
1 `timescale 1ps/1ps 2 3 4 module tb_ddr_top ; 5 6 reg ddr3_ref_clk ; 7 reg ddr3_rst_n ; 8 //bebug signals 9 reg wr_trig ; 10 wire c3_calib_done ; 11 12 //ddr3 interface 13 wire [15:0] mcb3_dram_dq ; 14 wire [12:0] mcb3_dram_a ; 15 wire [2:0] mcb3_dram_ba ; 16 wire mcb3_dram_ras_n ; 17 wire mcb3_dram_cas_n ; 18 wire mcb3_dram_we_n ; 19 wire mcb3_dram_odt ; 20 wire mcb3_dram_reset_n ; 21 wire mcb3_dram_cke ; 22 wire mcb3_dram_dm ; 23 wire mcb3_dram_udqs ; 24 wire mcb3_dram_udqs_n ; 25 wire mcb3_rzq ; 26 wire mcb3_zio ; 27 wire mcb3_dram_udm ; 28 wire mcb3_dram_dqs ; 29 wire mcb3_dram_dqs_n ; 30 wire mcb3_dram_ck ; 31 wire mcb3_dram_ck_n ; 32 33 34 35 36 parameter C3_MEMCLK_PERIOD = 3200; 37 38 39 initial begin 40 41 ddr3_ref_clk = 1; 42 ddr3_rst_n = 0; 43 #20000; 44 ddr3_rst_n = 1; 45 46 47 end 48 49 50 51 52 //produce debug signals 53 initial begin 54 wr_trig <= 0; 55 @(posedge c3_calib_done) 56 #100000 57 wr_trig <= 1; 58 #25600 59 wr_trig <= 0; 60 end 61 62 63 64 65 always #(C3_MEMCLK_PERIOD/2) ddr3_ref_clk = ~ddr3_ref_clk ; 66 67 68 ddr_top ddr_top_inst( 69 70 71 72 //sysyterm interface 73 .c3_sys_clk (ddr3_ref_clk ), 74 .c3_sys_rst_i (ddr3_rst_n ), 75 //ddr3 interface 76 .mcb3_dram_dq (mcb3_dram_dq ), 77 .mcb3_dram_a (mcb3_dram_a ), 78 .mcb3_dram_ba (mcb3_dram_ba ), 79 .mcb3_dram_ras_n (mcb3_dram_ras_n ), 80 .mcb3_dram_cas_n (mcb3_dram_cas_n ), 81 .mcb3_dram_we_n (mcb3_dram_we_n ), 82 .mcb3_dram_odt (mcb3_dram_odt ), 83 .mcb3_dram_reset_n (mcb3_dram_reset_n ), 84 .mcb3_dram_cke (mcb3_dram_cke ), 85 .mcb3_dram_dm (mcb3_dram_dm ), 86 .mcb3_dram_udqs (mcb3_dram_udqs ), 87 .mcb3_dram_udqs_n (mcb3_dram_udqs_n ), 88 .mcb3_rzq (mcb3_rzq ), 89 .mcb3_zio (mcb3_zio ), 90 .mcb3_dram_udm (mcb3_dram_udm ), 91 .mcb3_dram_dqs (mcb3_dram_dqs ), 92 .mcb3_dram_dqs_n (mcb3_dram_dqs_n ), 93 .mcb3_dram_ck (mcb3_dram_ck ), 94 .mcb3_dram_ck_n (mcb3_dram_ck_n ), 95 //debug signals 96 .wr_trig (wr_trig) , 97 .c3_calib_done (c3_calib_done) 98 99 ); 100 101 ddr3_model_c3 u_mem_c3( 102 .ck (mcb3_dram_ck), 103 .ck_n (mcb3_dram_ck_n), 104 .cke (mcb3_dram_cke), 105 .cs_n (1'b0), 106 .ras_n (mcb3_dram_ras_n), 107 .cas_n (mcb3_dram_cas_n), 108 .we_n (mcb3_dram_we_n), 109 .dm_tdqs ({mcb3_dram_udm,mcb3_dram_dm}), 110 .ba (mcb3_dram_ba), 111 .addr (mcb3_dram_a), 112 .dq (mcb3_dram_dq), 113 .dqs ({mcb3_dram_udqs,mcb3_dram_dqs}), 114 .dqs_n ({mcb3_dram_udqs_n,mcb3_dram_dqs_n}), 115 .tdqs_n (), 116 .odt (mcb3_dram_odt), 117 .rst_n (mcb3_dram_reset_n) 118 ); 119 120 // The PULLDOWN component is connected to the ZIO signal primarily to avoid the 121 // unknown state in simulation. In real hardware, ZIO should be a no connect(NC) pin. 122 PULLDOWN zio_pulldown3 (.O(zio3)); PULLDOWN rzq_pulldown3 (.O(rzq3)); 123 124 125 126 endmodule
(7)例化到顶层
module ddr_top(
//sysyterm interface
input c3_sys_clk ,
input c3_sys_rst_i ,
//ddr3 interface
inout [15:0] mcb3_dram_dq ,
output wire [12:0] mcb3_dram_a ,
output wire [2:0] mcb3_dram_ba ,
output wire mcb3_dram_ras_n ,
output wire mcb3_dram_cas_n ,
output wire mcb3_dram_we_n ,
output wire mcb3_dram_odt ,
output wire mcb3_dram_reset_n ,
output wire mcb3_dram_cke ,
output wire mcb3_dram_dm ,
inout mcb3_dram_udqs ,
inout mcb3_dram_udqs_n ,
inout mcb3_rzq ,
inout mcb3_zio ,
output wire mcb3_dram_udm ,
inout mcb3_dram_dqs ,
inout mcb3_dram_dqs_n ,
output wire mcb3_dram_ck ,
output wire mcb3_dram_ck_n ,
//debug
input wire wr_trig ,
input wire c3_calib_done
);
/*********************************************************************
*************************signals define********************************
**********************************************************************/
//ddr interface (write modle )
wire p0_wr_en ;
wire p0_cmd_en ;
wire [5:0] p0_cmd_bl ;
wire [2:0] p0_cmd_instr ;
wire [29:0]p0_cmd_addr ;
wire [63:0]p0_wr_data ;
wire [7:0] p0_wr_mask ;
/*********************************************************************
****************************main code ********************************
**********************************************************************/
ddr_drive ddr_drive_inst(
//systerm signals
.sclk (c3_clk0 ),
.s_rst_n (~c3_rst0 ),
//ddr interface
.p0_wr_en (p0_wr_en ),
.p0_cmd_en (p0_cmd_en ),
.p0_cmd_bl (p0_cmd_bl ),
.p0_cmd_instr (p0_cmd_instr),
.p0_cmd_addr (p0_cmd_addr ),
.p0_wr_data (p0_wr_data ),
.p0_wr_mask (p0_wr_mask ),
//debug signals
.wr_trig (wr_trig )
);
mig_39_2 # (
.C3_P0_MASK_SIZE(8),
.C3_P0_DATA_PORT_SIZE(64),
.C3_P1_MASK_SIZE(8),
.C3_P1_DATA_PORT_SIZE(64),
.DEBUG_EN(0),
.C3_MEMCLK_PERIOD(3200),//当前的时钟周期
.C3_CALIB_SOFT_IP("TRUE"),
.C3_SIMULATION("TRUE"),//仿真
.C3_RST_ACT_LOW(1),//复位信号的配置
.C3_INPUT_CLK_TYPE("SINGLE_ENDED"),//时钟模式
.C3_MEM_ADDR_ORDER("BANK_ROW_COLUMN"),//内存读取的顺序模式
.C3_NUM_DQ_PINS(16),
.C3_MEM_ADDR_WIDTH(13),
.C3_MEM_BANKADDR_WIDTH(3)
)
u_mig_39_2 (
//DDR3 的接口
.c3_sys_clk (c3_sys_clk), //input DDR3的参考时钟
.c3_sys_rst_i (c3_sys_rst_i), //input DDR3的复位信号
.mcb3_dram_dq (mcb3_dram_dq),
.mcb3_dram_a (mcb3_dram_a),
.mcb3_dram_ba (mcb3_dram_ba),
.mcb3_dram_ras_n (mcb3_dram_ras_n),
.mcb3_dram_cas_n (mcb3_dram_cas_n),
.mcb3_dram_we_n (mcb3_dram_we_n),
.mcb3_dram_odt (mcb3_dram_odt),
.mcb3_dram_cke (mcb3_dram_cke),
.mcb3_dram_ck (mcb3_dram_ck),
.mcb3_dram_ck_n (mcb3_dram_ck_n),
.mcb3_dram_dqs (mcb3_dram_dqs),
.mcb3_dram_dqs_n (mcb3_dram_dqs_n),
.mcb3_dram_udqs (mcb3_dram_udqs), // for X16 parts
.mcb3_dram_udqs_n (mcb3_dram_udqs_n), // for X16 parts
.mcb3_dram_udm (mcb3_dram_udm), // for X16 parts
.mcb3_dram_dm (mcb3_dram_dm),
.mcb3_dram_reset_n (mcb3_dram_reset_n),
//sppourt for user
.c3_clk0 (c3_clk0),//output 输出给用户提供的
.c3_rst0 (c3_rst0),//output 输出给用户提供的
.c3_calib_done (c3_calib_done),
.mcb3_rzq ( mcb3_rzq ),
.mcb3_zio (mcb3_zio ),
//P0,p1表示两个用户会接口
/*********************command path****************************/
.c3_p0_cmd_clk (c3_clk0 ), //命令FIFO的用户时钟。 FIFO信号是 在这个时钟的上升沿捕获。
.c3_p0_cmd_en (p0_cmd_en), //该高电平有效信号是用于写入的写入使能信号命令FIFO。
.c3_p0_cmd_instr (p0_cmd_instr), //当前指令的命令代码。 位0表示READ / WRITE选择,Bit 1为Auto预充电启用,位2代表刷新总是优先考虑
.c3_p0_cmd_bl (p0_cmd_bl), //当前用户字数的突发长度交易。 突发长度编码为0到63,代表1到64个用户词(例如,6'b00011 是一个突发长度4的交易)。 用户字宽等于端口宽度(例如,突发长度为3 64位端口传输3 x 64位用户字= 192位 总)。
/*当前事务的字节起始地址。地址
必须与端口大小对齐:
32位端口:低两位必须为0。
64位端口:低三位必须为0。
128位端口:低4位必须为0*/
.c3_p0_cmd_byte_addr (p0_cmd_addr),
.c3_p0_cmd_empty ( ), //这个命令FIFO的高电平有效空标志
.c3_p0_cmd_full ( ), //此高电平有效输出是命令的man标志
/*********************write cmd****************************/
.c3_p0_wr_clk (c3_clk0 ),//该信号是写数据FIFO的用户时钟
/*该高电平有效信号是写使能
用于写数据FIFO。它表明了
pX_wr_data上的值有效
加载到FIFO。数据已加载
pX_wr_clk的上升沿时
pX_wr_en = 1且pX_wr_full = 0。*/
.c3_p0_wr_en (p0_wr_en),
.c3_p0_wr_mask (p0_wr_mask),//写数据的掩码,
/*写入要写入的数据值
数据FIFO并发送到内存。 PX_SIZE
可以是32位,64位或128位,具体取决于
端口配置*/
.c3_p0_wr_data (p0_wr_data),
.c3_p0_wr_full ( ), //高有效的满信号
.c3_p0_wr_empty ( ),//高有效的空信号
/*写入数据FIFO的计数值。这个
输出表示有多少用户单词
在FIFO中(从1到64)。计数值为
0表示FIFO为空。这个信号
延迟的延迟比
pX_wr_empty标志。因此,FIFO
可能是空的或经历不足
即使计数不为0。*/
.c3_p0_wr_count ( ),
.c3_p0_wr_underrun ( ),//高电平有效,欠载标志。
.c3_p0_wr_error ( ),
/*********************read cmd****************************/
.c3_p0_rd_clk (c3_p0_rd_clk ),//该信号是du数据FIFO的用户时钟
.c3_p0_rd_en (c3_p0_rd_en ),
.c3_p0_rd_data (c3_p0_rd_data ),
.c3_p0_rd_full (c3_p0_rd_full ),
.c3_p0_rd_empty (c3_p0_rd_empty),
.c3_p0_rd_count (c3_p0_rd_count),
.c3_p0_rd_overflow (c3_p0_rd_overflow),
.c3_p0_rd_error (c3_p0_rd_error ),
/********************P1 user port************************/
.c3_p1_cmd_clk (c3_p1_cmd_clk ),
.c3_p1_cmd_en (c3_p1_cmd_en ),
.c3_p1_cmd_instr (c3_p1_cmd_instr ),
.c3_p1_cmd_bl (c3_p1_cmd_bl ),
.c3_p1_cmd_byte_addr (c3_p1_cmd_byte_addr),
.c3_p1_cmd_empty (c3_p1_cmd_empty ),
.c3_p1_cmd_full (c3_p1_cmd_full ),
.c3_p1_wr_clk (c3_p1_wr_clk),
.c3_p1_wr_en (c3_p1_wr_en),
.c3_p1_wr_mask (c3_p1_wr_mask),
.c3_p1_wr_data (c3_p1_wr_data),
.c3_p1_wr_full (c3_p1_wr_full),
.c3_p1_wr_empty (c3_p1_wr_empty),
.c3_p1_wr_count (c3_p1_wr_count),
.c3_p1_wr_underrun (c3_p1_wr_underrun),
.c3_p1_wr_error (c3_p1_wr_error),
.c3_p1_rd_clk (c3_p1_rd_clk),
.c3_p1_rd_en (c3_p1_rd_en),
.c3_p1_rd_data (c3_p1_rd_data),
.c3_p1_rd_full (c3_p1_rd_full),
.c3_p1_rd_empty (c3_p1_rd_empty),
.c3_p1_rd_count (c3_p1_rd_count),
.c3_p1_rd_overflow (c3_p1_rd_overflow),
.c3_p1_rd_error (c3_p1_rd_error)
);
endmodule