Xilinx MIG核读写DDR3内存,连续读写内存的正确方法(时序)及代码

您所在的位置:网站首页 ddr3内存读取速度 Xilinx MIG核读写DDR3内存,连续读写内存的正确方法(时序)及代码

Xilinx MIG核读写DDR3内存,连续读写内存的正确方法(时序)及代码

2024-07-10 21:58| 来源: 网络整理| 查看: 265

关于Xilinx 2020.1新建工程时配置MIG核的完整步骤,请参阅:https://blog.csdn.net/ZLK1214/article/details/111349678

MIG核里面有两个通道:命令通道和数据通道。这两个通道是相互独立的,互不影响。 命令通道:要发送的命令由ddr3_app_cmd指定(0号命令是写内存,1号命令是读内存),ddr3_app_en拉高就开始发送命令。 数据通道:要发送的数据由ddr3_app_wdf_data指定,ddr3_app_wdf_wren拉高就开始往FIFO里面送入数据。 MIG里面是严格一个时钟(ddr3_ui_clk)发送一个命令(或写一个数据)。ddr3_app_en如果一直拉高,那么就一直重复发送命令。拉高了多少个时钟周期,就发送多少条命令。同样,ddr3_app_wdf_wren如果一直拉高,就一直重复往FIFO里面写入数据。编写程序的时候要特别注意这一点。

如下图所示是MIG连续写DDR3内存时的时序。ILA的采样周期刚好就是ddr3_ui_clk(100MHz),横向时间单位为10ns。 从红竖线(8192时刻)开始写内存,发送写命令(ddr3_app_cmd=0且ddr3_app_en=1),写的内存地址是ddr3_app_addr=0,同时往FIFO里面送入要写的数据(ddr3_app_wdf_wren=1,wdf的全称为write data FIFO)。第8193时刻,读到ddr3_app_rdy为1说明写命令发送成功,读到ddr3_app_wdf_rdy=1说明数据成功送入FIFO。这两位同时为1表示写内存成功。 第8193时刻又发出了写8号地址的写命令,往FIFO里面塞入数据0x0008。然而,第8194时刻读到ddr3_app_rdy为0,说明写命令发送失败了!!同时读到ddr3_app_wdf_rdy为1,说明往FIFO里面送入数据成功了。这个时候就要注意了,写命令发送失败但FIFO送入数据成功,接下来写命令应该重新发送,但数据不能再往FIFO里面送了!!!也就是图中红框的部分,在第8194时刻必须把ddr3_app_wdf_wren拉低,停止送入数据,不然的话,这种情况下就会连续往FIFO里面送入三个0x0008,导致后面的地址0x0000010(16号地址),0x0000018(24号地址)都被写入0x0008这个数据!!!如果不懂的话,是非常容易犯这个错误的。 写8号地址的命令发了两次都失败了,第三次终于发送成功了,ddr3_app_rdy为1。在8196时刻可以发送新的写命令了,向FIFO里面写入新的数据。

连续读内存也是类似的。如下图所示是一种错误的做法。只要ddr3_app_en为1,就一直在发命令,ddr3_app_addr一直为0就发了一大串读0号地址的命令。

那改成发一个读命令,等到数据读出来后(ddr3_app_rd_data_valid=1),再发下一个命令,这样就能读出正确的结果,但是抓了波形图一看,中间的等待时间很长,每读一个地址都要等待很久,非常浪费时间。

于是我们可以边发命令,边接收数据。一开始就从0地址开始一直往后发命令:0,8,16,24,36,48…… 当ddr3_app_rd_data_valid=1时,我们已经发到0xb0(176)地址了,这个时候0号地址的数据才读出来,于是一个一个接收,如下图所示。

这样连续读数据就会比刚才快很多很多。值得注意的是,接收完最后一个数据后,ddr3_app_rd_data_valid会变为0,表明数据已接收完毕。

我们来看看最终的代码:

这里程序有一个失误,就是ddr3_ui_clk_sync_rst是一个同步复位信号,不应该是异步复位。代码里面写成了always @(posedge ddr3_ui_clk, posedge ddr3_ui_clk_sync_rst) begin,把ddr3_ui_clk_sync_rst用成了一个异步的复位信号,这是不对的。应该把always敏感列表里面的posedge ddr3_ui_clk_sync_rst去掉,写成always @(posedge ddr3_ui_clk) begin才对。不过笔者在实际运行时并没有发现有什么不对的地方。

module main( input clock, // 50MHz外部晶振 // DDR3引脚 inout [15:0] ddr3_dq, inout [1:0] ddr3_dqs_n, inout [1:0] ddr3_dqs_p, output [13:0] ddr3_addr, output [2:0] ddr3_ba, output ddr3_ras_n, output ddr3_cas_n, output ddr3_we_n, output ddr3_reset_n, output ddr3_ck_p, output ddr3_ck_n, output ddr3_cke, output ddr3_cs_n, output [1:0] ddr3_dm, output ddr3_odt, input [3:0] keys, output [3:0] leds, // 4个LED灯 output uart_tx // 串口发送引脚 ); parameter SYSCLK = 50000000; wire nrst; Reset #(SYSCLK) reset(clock, !keys[0], nrst); wire clock200; wire locked; clk_wiz_0 clk_wiz_0( .reset(!nrst), .clk_in1(clock), // 输入50MHz时钟 .clk_out1(clock200), // 输出200MHz时钟 .locked(locked) // 该信号表示输出时钟是否已稳定 ); reg [27:0] ddr3_app_addr; reg [2:0] ddr3_app_cmd; reg ddr3_app_en; reg [127:0] ddr3_app_wdf_data; // 因为burst=8, data_width=16, 所以wdf_data的宽度为8*16=128 reg ddr3_app_wdf_end; reg ddr3_app_wdf_wren; wire [127:0] ddr3_app_rd_data; // 这个也是128位 wire ddr3_app_rd_data_end; wire ddr3_app_rd_data_valid; wire ddr3_app_rdy; wire ddr3_app_wdf_rdy; wire ddr3_app_sr_active; wire ddr3_app_ref_ack; wire ddr3_app_zq_ack; wire ddr3_ui_clk; wire ddr3_ui_clk_sync_rst; wire ddr3_init_calib_complete; wire [11:0] ddr3_device_temp; mig_7series_0 mig_7series_0( .ddr3_dq(ddr3_dq), .ddr3_dqs_n(ddr3_dqs_n), .ddr3_dqs_p(ddr3_dqs_p), .ddr3_addr(ddr3_addr), .ddr3_ba(ddr3_ba), .ddr3_ras_n(ddr3_ras_n), .ddr3_cas_n(ddr3_cas_n), .ddr3_we_n(ddr3_we_n), .ddr3_reset_n(ddr3_reset_n), .ddr3_ck_p(ddr3_ck_p), // DDR3内存时钟输出: 400MHz .ddr3_ck_n(ddr3_ck_n), .ddr3_cke(ddr3_cke), .ddr3_cs_n(ddr3_cs_n), .ddr3_dm(ddr3_dm), .ddr3_odt(ddr3_odt), .sys_clk_i(clock200), // 系统时钟输入: 200MHz .clk_ref_i(clock200), // 参考时钟输入: 200MHz .app_addr(ddr3_app_addr), .app_cmd(ddr3_app_cmd), .app_en(ddr3_app_en), .app_wdf_data(ddr3_app_wdf_data), .app_wdf_end(ddr3_app_wdf_end), .app_wdf_mask(16'h0), // 8突发*每个数据2字节=16字节, 所以mask有16位 .app_wdf_wren(ddr3_app_wdf_wren), .app_rd_data(ddr3_app_rd_data), .app_rd_data_end(ddr3_app_rd_data_end), .app_rd_data_valid(ddr3_app_rd_data_valid), .app_rdy(ddr3_app_rdy), .app_wdf_rdy(ddr3_app_wdf_rdy), .app_sr_req(1'b0), .app_ref_req(1'b0), .app_zq_req(1'b0), .app_sr_active(ddr3_app_sr_active), .app_ref_ack(ddr3_app_ref_ack), .app_zq_ack(ddr3_app_zq_ack), .ui_clk(ddr3_ui_clk), // 用户时钟输出: 因为选的是4:1, 所以ddr3_ck_p:ddr3_ui_clk=4:1, ddr3_ui_clk是100MHz .ui_clk_sync_rst(ddr3_ui_clk_sync_rst), // 用户程序复位输出 .init_calib_complete(ddr3_init_calib_complete), .device_temp(ddr3_device_temp), .sys_rst(locked) // 复位输入: 当倍频器时钟未稳定时, 使MIG处于复位状态 ); // FPGA内部的分布式RAM // Depth: 112, Data Width: 128, Memory Type: Single Port RAM reg [6:0] dist_mem_a; reg [127:0] dist_mem_d; reg dist_mem_we; // 写使能 wire [127:0] dist_mem_spo; dist_mem_gen_0 dist_mem_gen_0( .clk(ddr3_ui_clk), .a(dist_mem_a), .d(dist_mem_d), .spo(dist_mem_spo), .we(dist_mem_we) ); // 串口单字节发送 wire uart_tx_request; wire [7:0] uart_tx_data; wire uart_tx_ready; wire uart_sent; UARTTransmitter #(SYSCLK, 115200) uart_transmitter(clock, locked, uart_tx, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent); // 串口多字节组合发送 reg uart_bytearray_tx_mode; reg [159:0] uart_bytearray_tx_data; reg uart_bytearray_tx_request; reg [7:0] uart_bytearray_tx_size; wire uart_bytearray_tx_ready; wire uart_bytearray_sent; ByteArrayTransmitter #(20) uart_bytearray_transmitter(clock, locked, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent, uart_bytearray_tx_mode, uart_bytearray_tx_request, uart_bytearray_tx_data, uart_bytearray_tx_size, uart_bytearray_tx_ready, uart_bytearray_sent); reg [3:0] i; reg uart_state; assign leds = i; always @(posedge ddr3_ui_clk, posedge ddr3_ui_clk_sync_rst) begin if (ddr3_ui_clk_sync_rst) begin ddr3_app_en


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3