您的位置:首页 > 健康 > 养生 > DDR3的使用(三)利用XILINX MIGIP核(native)读写DDR3代码分析

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

2024/12/23 20:30:28 来源:https://blog.csdn.net/weixin_42306014/article/details/140697687  浏览:    关键词: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)
beginif(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;elseerror <= error;//写入数据和读出数据不一致
end
always@(posedge mem_clk or posedge rst)
beginif(rst)beginwr_burst_data_reg <= {MEM_DATA_BITS{1'b0}};wr_cnt <= 8'd0;endelse if(state == MEM_WRITE)beginif(wr_burst_data_req)beginwr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}};//写入数据wr_cnt <= wr_cnt + 8'd1;endelse if(wr_burst_finish)wr_cnt <= 8'd0;end
end
//读数据
always@(posedge mem_clk or posedge rst)
beginif(rst)beginrd_cnt <= 8'd0;endelse if(state == MEM_READ)beginif(rd_burst_data_valid)beginrd_cnt <= rd_cnt + 8'd1;endelse if(rd_burst_finish)rd_cnt <= 8'd0;endelserd_cnt <= 8'd0;
end
//状态机
always@(posedge mem_clk or posedge rst)
beginif(rst)beginstate <= 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;endelsebegincase(state)IDLE:beginstate <= MEM_WRITE;wr_burst_req <= 1'b1;wr_burst_len <= 10'd128;endMEM_WRITE:beginif(wr_burst_finish)beginstate <= MEM_READ;wr_burst_req <= 1'b0;rd_burst_req <= 1'b1;rd_burst_len <= 10'd128;rd_burst_addr <= wr_burst_addr;//地址endendMEM_READ:beginif(rd_burst_finish)beginstate <= MEM_WRITE;wr_burst_req <= 1'b1;wr_burst_len <= 10'd128;rd_burst_req <= 1'b0;wr_burst_addr <= wr_burst_addr + 128;endenddefault:state <= IDLE;endcaseend
endendmodule

请添加图片描述
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:beginif(app_rdy)beginapp_addr_r <= app_addr_r + 'b1000;if(wr_addr_cnt == wr_burst_len - 1)beginapp_wdf_end_r <= 1'b0;app_en_r <= 1'b0;endelsebeginwr_addr_cnt <= wr_addr_cnt + 1;endend					if(wr_burst_data_req)begin					if(wr_data_cnt == wr_burst_len - 1)beginstate <= MEM_WRITE_WAIT;endelsebeginwr_data_cnt <= wr_data_cnt + 1;endend	end			

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

			MEM_READ:beginif(app_rdy)beginapp_addr_r <= app_addr_r + 8;if(rd_addr_cnt == rd_burst_len - 1)beginstate <= MEM_READ_WAIT;rd_addr_cnt <= 0;app_en_r <= 1'b0;endelserd_addr_cnt <= rd_addr_cnt + 1;endif(app_rd_data_valid)beginif(rd_data_cnt == rd_burst_len - 1)beginrd_data_cnt <= 0;state <= READ_END;endelsebeginrd_data_cnt <= rd_data_cnt + 1;endendendMEM_READ_WAIT:beginif(app_rd_data_valid)beginif(rd_data_cnt == rd_burst_len - 1)beginrd_data_cnt <= 0;state <= READ_END;endelsebeginrd_data_cnt <= rd_data_cnt + 1;endendend

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com