本文相对简单,只供自己看看就行。从其它的博客找了个代码,然后记录下仿真波形。

1. 功能

        直接使用quartus生成的DDR2 IP核,然后实现循环 -->写入burst长度的数据后读出。

        代码数据的传输是32位,实际使用了两片IC。因此IP核也是32位交互。

2.信号分析

        burst=4,是IC固有的=预取值。而代码中LEN为用户一次想要读取数据的长度的。与local_size和burst_length无关。

        分为4个状态: IDLE、WRITER、WRITING、READR、READING。WRITER和READR状态下,开始读写burst的第一个数据,其余的在WRITING和READING状态读写。

         local_ready:很重要的一个指示信号。要进行读写操作,必须在IP核(ddr2)准备好的情况下进行。因此,需要一直对此信号进行检测判断。

        写入地址(mem_local_addr),一开始为1,当读到的数据长度rdatalen=5时(初始值=1),mem_local_addr <= mem_local_addr + 4;

命令

DDR2 IP核调式记录2-LMLPHP

连续写

DDR2 IP核调式记录2-LMLPHP

连续读

DDR2 IP核调式记录2-LMLPHP

读完写

DDR2 IP核调式记录2-LMLPHP

3. 仿真波形分析

如下图所示:每次先写入连续4个数据,(例如1,2,3,4),然后再依次读出。

DDR2 IP核调式记录2-LMLPHP

读过程及下一次写

DDR2 IP核调式记录2-LMLPHP

4. 附代码

顶层代码:

module ddr2wrtest (
	input 					clk 			,    // Clock
	input 					global_reset_n 	,  // Asynchronous reset active low


	output  [ 12: 0]		mem_addr 		,	
	output  [  2: 0] 		mem_ba 			,	
	output					mem_cas_n 		,	
	output					mem_cke 		,	
	inout					mem_clk 		,	
	inout 					mem_clk_n 		,	
	output 					mem_cs_n 		,	
	output 	[  1: 0]		mem_dm 			,	
	inout	[ 15: 0]		mem_dq 			,	
	inout	[  1: 0]		mem_dqs 		,	
	output					mem_odt 		,	
	output					mem_ras_n 		,	
	output					mem_we_n 			
	
);


	parameter 			LEN 	= 8; 				// 突发长度 4

	parameter 			IDEL 		= 5'b00001;		// 空闲态
	parameter 			WRITER 		= 5'b00010;		// 准备写
	parameter 			WRITING 	= 5'b00100;		// 写状态
	parameter 			READR 	 	= 5'b01000;		// 准备读
	parameter 			READING 	= 5'b10000;		// 读状态

	reg	 [  5: 0]		state;
//**********************与IP相连部分********************//
	reg [ 24: 0]	mem_local_addr		=1	;
	wire 				mem_local_init_done		;	
	wire 				local_burstbegin_sig	;								
	wire [ 31: 0]	mem_local_rdata 		;
	wire 				mem_local_rdata_valid	;
	wire 				mem_local_read_req		;
	wire 				mem_local_ready			;							
	wire [  3: 0]	mem_local_size			;
	reg  [ 31: 0]	mem_local_wdata			;
	wire 				mem_local_write_req		;
	wire 				phy_clk 				;
	wire 				reset_phy_clk_n 		;
//*****************************************************//
	wire 				rst_n 					;

	reg  [  3: 0] 		rdatalen 				; // 读突发长度计数器
	reg  [  3: 0] 		wdatalen 				; // 写突发长度计数器
	reg  [100: 0]		state_name				; // 状态名

	assign rst_n = reset_phy_clk_n&&mem_local_init_done;
	assign mem_local_size = LEN;
	assign local_burstbegin_sig = ((state == WRITER&&mem_local_ready) || (state == READR&&mem_local_ready))?1:0;
	assign mem_local_write_req  = (state == WRITING)?1:0;
	assign mem_local_read_req 	= (state == READR&&mem_local_ready)?1:0;
	
	ddr2 ddr2_inst
	    (
//==============================《				   》===============================
	      .aux_full_rate_clk 	(						),	//全速率时钟
	      .aux_half_rate_clk 	(						),	//半速率时钟
//==============================《    用户操作信号   》===============================
	      .global_reset_n		(global_reset_n 		),	//全局复位
	      .local_address 		(mem_local_addr			),	//当前操作地址
	      .local_be 			(8'hff					),	//数据掩码
	      .local_burstbegin 	(local_burstbegin_sig	),	//突发起始信号
	      .local_init_done 		(mem_local_init_done	),	//初始化完成信号
	      .local_rdata 			(mem_local_rdata 		),	//读数据总线
	      .local_rdata_valid 	(mem_local_rdata_valid	),	//读有效标志
	      .local_read_req 		(mem_local_read_req		),	//读请求,保持一个时钟周期
	      .local_ready 			(mem_local_ready		),	//接受到请求
	      .local_refresh_ack 	(						),	//自动刷新
	      .local_size 			(mem_local_size			),	//突发长度
	      .local_wdata 			(mem_local_wdata		),	//写数据总线
	      .local_write_req 		(mem_local_write_req	),	//写请求
//==============================《  与DDR2相连的信号  》===============================
	      .mem_addr 			(mem_addr 				),	//地址总线
	      .mem_ba 				(mem_ba					),	//bank地址
	      .mem_cas_n 			(mem_cas_n				),	//行选通
	      .mem_cke 				(mem_cke				),	//时钟使能
	      .mem_clk 				(mem_clk 				),	//操作时钟
	      .mem_clk_n 			(mem_clk_n				),	//反向时钟
	      .mem_cs_n 			(mem_cs_n 				),	//片选信号
	      .mem_dm 				(mem_dm 				),	//DDR2数据屏蔽信号
	      .mem_dq 				(mem_dq 				),	//数据总线
	      .mem_dqs 				(mem_dqs 				),	//数据选取脉冲信号
	      .mem_odt 				(mem_odt 				),	//片内终结信号
	      .mem_ras_n 			(mem_ras_n 				),	//行选通
	      .mem_we_n 			(mem_we_n 				),	//使能
//==============================《    用户操作信号    》===============================
	      .phy_clk 				(phy_clk 				),	//提供给用户的操作时钟
	      .pll_ref_clk 			(clk 					),	//给ddr2的输入时钟
	      .reset_phy_clk_n 		(reset_phy_clk_n 		),	//提供给用户的复位信号
	      .reset_request_n 		(						),	//当PLL锁定后为低电平
	      .soft_reset_n 		(1'b1 					)	//软复位,不包括PLL的复位
	    );

	    always@(*)
	    case (state)
	    	IDEL  	:	state_name = "IDEL";
			WRITER 	:	state_name = "WRITER"		;		
			WRITING	:	state_name = "WRITING"		;		
			READR 	:	state_name = "READR"		;			
			READING	:	state_name = "READING";
	    
	    	default : /* default */;
	    endcase

		always@(posedge phy_clk or negedge rst_n)begin 
		if(!rst_n)
			state <= IDEL;
		else
			case (state)
				IDEL 	:	state <= WRITER;

				WRITER 	: 	if(mem_local_ready)
								state <= WRITING;
							else
								state <= WRITER;

				WRITING : 	if(wdatalen == LEN)
								state <= READR;
							else
								state <= state;
				READR 	: 	if(mem_local_ready)
								state <= READING;
							else
								state <= READR;

				READING : 	if(rdatalen == LEN)
								state <= IDEL;
							else
								state <= state;		
				default : state <= state;
			endcase
		end

		// 写的数据自加1
		always@(posedge phy_clk or negedge rst_n)begin 
		if(!rst_n)
			mem_local_wdata <= 1;
		else if(state == WRITING && mem_local_ready)
			mem_local_wdata <= mem_local_wdata + 1;
		else
			mem_local_wdata <= mem_local_wdata;

		end

		// 当前写的数据突发长度
		always@(posedge phy_clk or negedge rst_n)begin 
		if(!rst_n) 
			wdatalen <= 1;
		else if(state == WRITING && mem_local_ready)
			wdatalen <= wdatalen + 1;
		else if(state == WRITING && !mem_local_ready)
			wdatalen <= wdatalen ;
		else
			wdatalen <= 1;
		end

		// 当前读的数据突发长度
		always@(posedge phy_clk or negedge rst_n)begin 
		if(!rst_n) 
			rdatalen <= 1;
		else if(state == READING && mem_local_rdata_valid)
			rdatalen <= rdatalen + 1;
		else
			rdatalen <= 1;
		end

		// 操作地址自加
		always@(posedge phy_clk or negedge rst_n)begin 
		if(!rst_n)
			mem_local_addr <= 1;
		else if(rdatalen == LEN)
			mem_local_addr <= mem_local_addr + LEN;
		end

endmodule

测试代码

`timescale 1ns/1ps
module test_tb ();

reg				clk 		;
reg				global_reset_n;

wire  	[ 12: 0]	mem_addr 	;
wire  	[  2: 0] 	mem_ba 		;
wire				mem_cas_n 	;
wire				mem_cke 	;
wire				mem_clk 	;
wire 				mem_clk_n 	;
wire 				mem_cs_n 	;
wire 	[  1: 0]	mem_dm 		;
wire	[ 15: 0]	mem_dq 		;
wire	[  1: 0]	mem_dqs 	;
wire				mem_odt 	;
wire				mem_ras_n 	;
wire				mem_we_n 	;


 ddr2wrtest u1 (
			.clk 			(clk 			),    // Clock
			.global_reset_n (global_reset_n ) ,  // Asynchronous reset active low
			.mem_addr 		(mem_addr 		),	
			.mem_ba 		(mem_ba 		),	
			.mem_cas_n 		(mem_cas_n 		),	
			.mem_cke 		(mem_cke 		),	
			.mem_clk 		(mem_clk 		),	
			.mem_clk_n 		(mem_clk_n 		),	
			.mem_cs_n 		(mem_cs_n 		),	
			.mem_dm 		(mem_dm 		),	
			.mem_dq 		(mem_dq 		),	
			.mem_dqs 		(mem_dqs 		),	
			.mem_odt 		(mem_odt 		),	
			.mem_ras_n 		(mem_ras_n 		),	
			.mem_we_n 		(mem_we_n 		)	
	
);

 ddr2_mem_model mem (
    	.mem_dq      (mem_dq),
        .mem_dqs     (mem_dqs),
        .mem_dqs_n   (mem_dqs_n),
        .mem_addr    (mem_addr),
        .mem_ba      (mem_ba),
        .mem_clk     (mem_clk),
        .mem_clk_n   (mem_clk_n),
        .mem_cke     (mem_cke),
        .mem_cs_n    (mem_cs_n),
        .mem_ras_n   (mem_ras_n),
        .mem_cas_n   (mem_cas_n),
        .mem_we_n    (mem_we_n),
        .mem_dm      (mem_dm),
        .mem_odt     (mem_odt)
    );
always #10 clk = ~clk;
initial begin 
	clk = 0;
	global_reset_n = 0;
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	global_reset_n = 1;
end
endmodule
09-05 04:18