DDR3的使用(三)利用XILINX MIGIP核(native)读写DDR3代码分析

我们前面观察了下利用ip核读写ddr3芯片,这一节我们对上一节代码进行初步解析,以起到理解mig ip核的作用。
请添加图片描述
1.我们看到代码分为三个模块,top层封装了mem_burst和mem_test及ip核。我们首先看看mem_test部分代码:

module mem_test
#(
	parameter MEM_DATA_BITS = 64,
	parameter ADDR_BITS = 24
)
(
	input rst,                                         /*复位*/
	input mem_clk,                                     /*接口时钟*/
	output reg rd_burst_req,                          /*读请求*/
	output reg wr_burst_req,                          /*写请求*/
	output reg[9:0] rd_burst_len,                     /*读数据长度*/
	output reg[9:0] wr_burst_len,                     /*写数据长度*/
	output reg[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
	output reg[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
	input rd_burst_data_valid,                         /*读出数据有效*/
	input wr_burst_data_req,                           /*写数据信号*/
	input[MEM_DATA_BITS - 1:0] rd_burst_data,          /*读出的数据*/
	output[MEM_DATA_BITS - 1:0] wr_burst_data,         /*写入的数据*/
	input rd_burst_finish,                             /*读完成*/
	input wr_burst_finish,                             /*写完成*/
	output reg error
);
localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_WRITE  = 3'd2;

reg[2:0] state;
reg[7:0] wr_cnt;
reg[MEM_DATA_BITS - 1:0] wr_burst_data_reg;
assign wr_burst_data = wr_burst_data_reg;
reg[7:0] rd_cnt;
always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	   error <= 1'b0;
	else if((state == MEM_READ) && rd_burst_data_valid && (rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}}))
	   error <= 1'b1;
	else
	   error <= error;//写入数据和读出数据不一致
end
always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		wr_burst_data_reg <= {MEM_DATA_BITS{1'b0}};
		wr_cnt <= 8'd0;
	end
	else if(state == MEM_WRITE)
	begin
		if(wr_burst_data_req)
			begin
				wr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}};//写入数据
				wr_cnt <= wr_cnt + 8'd1;
			end
		else if(wr_burst_finish)
			wr_cnt <= 8'd0;
	end
end
//读数据
always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		rd_cnt <= 8'd0;
	end
	else if(state == MEM_READ)
	begin
		if(rd_burst_data_valid)
			begin
				rd_cnt <= rd_cnt + 8'd1;
			end
		else if(rd_burst_finish)
			rd_cnt <= 8'd0;
	end
	else
		rd_cnt <= 8'd0;
end
//状态机
always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		state <= IDLE;
		wr_burst_req <= 1'b0;
		rd_burst_req <= 1'b0;
		rd_burst_len <= 10'd128;//数据长度
		wr_burst_len <= 10'd128;
		rd_burst_addr <= 0;
		wr_burst_addr <= 0;
	end
	else
	begin
		case(state)
			IDLE:
			begin
				state <= MEM_WRITE;
				wr_burst_req <= 1'b1;
				wr_burst_len <= 10'd128;
			end
			MEM_WRITE:
			begin
				if(wr_burst_finish)
				begin
					state <= MEM_READ;
					wr_burst_req <= 1'b0;
					rd_burst_req <= 1'b1;
					rd_burst_len <= 10'd128;
					rd_burst_addr <= wr_burst_addr;//地址
				end
			end
			MEM_READ:
			begin
				if(rd_burst_finish)
				begin
					state <= MEM_WRITE;
					wr_burst_req <= 1'b1;
					wr_burst_len <= 10'd128;
					rd_burst_req <= 1'b0;
					wr_burst_addr <= wr_burst_addr + 128;
				end
			end
			default:
				state <= IDLE;
		endcase
	end
end

endmodule

请添加图片描述
2.我们看到82-124行这个状态机主要产生读写请求,读写长度和读写地址。程序进入先进入idle状态确定了写请求和写长度,再进入写数据状态,在写数据状态等待写数据完成,如果没有写完wr burst reg=1,wr burst len=128,如果wr_burst_finish进入读数据状态,这是rd_burst_req=1,rd_burst_len <= 10’d128;意思就是读取128个数据,读完后写地址增加128,。总的流程就是写入128个数据,独处128个数据,地址增加128,继续写入读出,并且将读出数据与写入数据做对比。
请添加图片描述
3.我们看到32-38行就是将读出的数据与写入的数据进行比较,如果不一样会拉高电平,点亮led灯。
39-55行产生了需要写入ddr3的值。58-74是一个读数据计数器。这里重点讲下37行,{(MEM_DATA_BITS/8){rd_cnt}}中MEM_DATA_BITS=512,所以也就等效于{64{rd_cnt}},意思就是64个rd_cnt,rd_cnt是8位数据,所以{64{rd_cnt}}就是将rd_cnt复制64次连在一起,一共512位。
顺便问下为什么我们写入数据是512位而不是其他?这个就要结合我们上一节时钟结构了,我们ddr3接口传输数据速率位800M2=1600M,我们逻辑代码使用mig ip给的ui时钟位200M,1600/200=8,也就说ddr3接口处数据速率是我们逻辑代码处速率的8倍,如果速率要一样,那么接口处位宽为64位,我们只需要给一个648=512位的数据,速率就一样了。这就是512位数据位宽的由来。
4.我们再看看mem_burst模块
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
可以看到,这个状态机主要是和mig ip接口进行数据交互,接收到写数据请求后进入写数据状态,由于写数据与写地址及指令是分开的,所以写数据和写地址同时进行。

MEM_WRITE:
			begin
				if(app_rdy)
				begin
					app_addr_r <= app_addr_r + 'b1000;
					if(wr_addr_cnt == wr_burst_len - 1)
					begin
						app_wdf_end_r <= 1'b0;
						app_en_r <= 1'b0;
					end
					else
					begin
						wr_addr_cnt <= wr_addr_cnt + 1;
					end
					
				end					
				if(wr_burst_data_req)
				begin					
					if(wr_data_cnt == wr_burst_len - 1)
					begin
						state <= MEM_WRITE_WAIT;
					end
					else
					begin
						wr_data_cnt <= wr_data_cnt + 1;
					end
				end	
			end			

如果数据写入128个就进入MEM_WRITE_WAIT,继续写地址,直到写地址完成条入到idle状态。
同理,收到读取指令时进入到读数据状态,一边写入地址,一百年读取数据,如果写入地址完成读取数据未完成则在MEM_READ_WAIT状态中继续读取数据,直到读完数据后进入idle状态。

			MEM_READ:
			begin
				if(app_rdy)
				begin
					app_addr_r <= app_addr_r + 8;
					if(rd_addr_cnt == rd_burst_len - 1)
					begin
						state <= MEM_READ_WAIT;
						rd_addr_cnt <= 0;
						app_en_r <= 1'b0;
					end
					else
						rd_addr_cnt <= rd_addr_cnt + 1;
				end
				
				if(app_rd_data_valid)
				begin
					if(rd_data_cnt == rd_burst_len - 1)
					begin
						rd_data_cnt <= 0;
						state <= READ_END;
					end
					else
					begin
						rd_data_cnt <= rd_data_cnt + 1;
					end
				end
			end
			MEM_READ_WAIT:
			begin
				if(app_rd_data_valid)
				begin
					if(rd_data_cnt == rd_burst_len - 1)
					begin
						rd_data_cnt <= 0;
						state <= READ_END;
					end
					else
					begin
						rd_data_cnt <= rd_data_cnt + 1;
					end
				end
			end

以上就是大概思路。mig ip接口端具体的信号及其含义如下:
请添加图片描述
具体的时序,我们后面有时间进行挨个仿真测试。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值