1194|0

866

帖子

3

TA的资源

版主

楼主
 

08、国产FPGA 正点原子DFPGL22G开发板测评【学习篇】DDR读写实验 [复制链接]

 
本帖最后由 1nnocent 于 2023-2-18 10:55 编辑

1、简介

    DDR3 SDRAM(Double-Data-Rate Three Synchronous Dynamic Random Access Memory)是 DDRSDRAM 的第三代产品,相较于 DDR 和 DDR2, DDR3 有更高的运行性能与更低的电压。 DDR SDRAM 是在 SDRAM 技术的基础上发展改进而来的,同 SDRAM 相比, DDR SDRAM 的最大特点是双沿触发,即在时钟的上升沿和下降沿都能进行数据采集和发送。同样的工作时钟, DDR SDRAM 的读写速度可以比传统的 SDRAM 快一倍。

    PGL22 系列 FPGA 自带了 DDR3 控制器的硬核,用户可以直接借助 IP 核来实现对 DDR3 的读写操作,从而大大降低了 DDR3 的开发难度。HMIC_H IP 是深圳市紫光同创电子有限公司 FPGA 产品中用于实现对 SDRAM 读写而设计的 IP,以下为HMIC_H IP 系统框图:

     HMIC_H IP 包括了 DDR Controller、 DDR PHY 和 PLL,用户通过 AXI4 接口实现数据的读写,通过APB 接口可配置 DDR Controller 内部寄存器, PLL 用于产生需要的各种时钟。

    AXI4 接口: HMIC_H IP 提供三组 AXI4 Host Port: AXI4 Port0(128bit)、 AXI4 Port1(64bit)、 AXI4Port2(64bit)。用户通过 HMIC_H IP 界面可以选择使能这三组 AXI4 Port。三组 AXI4 Host Port 均为标准AXI4 接口。
    APB 接口: HMIC_H IP 提供一个 APB 配置接口,通过该接口,可配置 DDR Controller 内部寄存器。HMIC_H IP 可通过 APB 接口对内部 DDRC 配置寄存器进行读写,在初始化阶段, IP 将配置 DDRC 内部的配置寄存器,如果用户需要读写 DDRC 内部寄存器,需要在初始化完成后进行操作。 由于 IP 初始化阶段已将 DDRC 内部寄存器进行了正确的配置,因此不建议用户在初始化完成后随意更改配置寄存器的值。

    AXI 总线共有 5 个独立的通道,分别是 read address channel (ARxxx), write address channel(AWxxx),read data channel(Rxxx), write data channel(Wxxx), write response channel(Bxxx)。 每一个 AXI 传输通道都是单方向的,且都包含一个信息信号和一个双路的 VALID、 READY 握手机制。信息源通过 VALID 信号来指示通道中的数据和控制信息什么时候有效。目地源用 READY 信号来表示何时能够接收数据。读数据和写数据通道都包括一个 LAST 信号,用来指明一个事物传输的最后一个数据。

    主机/设备之间的握手过程以及 READY 和 VALID 握手信号的关系如下:全部 5 个通道使用相同的 VALID/READY 握手机制传输数据及控制信息。传输源产生 VALID 信号来指明何时数据或控制信息有效。而目地源产生 READY 信号来指明已经准备好接受数据或控制信息。传输发生在 VALID 和 READY 信号同时为高的时候。 VALID 和 READY 信号的出现有三种关系。 图中箭头处信息传输发生。分别为VALID 先变高 READY 后变高:

 

READY 先变高 VALID 后变高:

 

VALID 和 READY 信号同时变高:

 

2、试验任务

    先向 DDR3 的存储器中写入 5120 个数据,写完之后再从存储器中读取相同地址的数据。若初始化成功, 则 LED0 常亮,否则 LED0 不亮; 若读取的值全部正确则 LED1 常亮,否则 LED1闪烁。

3、硬件设计

    ATK-DFPGL22G 开发板上使用了一片南亚的 DDR3 颗粒 NT5CC256M16,硬件原理图如下图所示。

 

4、程序设计

    FPGA 调用 ddr3 测试数据模块向 ddr3 控制模块写入数据,写完之后 ddr 测试数据模块从 ddr3 控制模块读出所写入的数据,并判断读出的数据与写入的数据是否相同,如果相同则 LED1 灯常亮,否则 LED1 灯闪烁。总体框图:

 

    顶层模块(ddr3_rw_top):ddr3 控制器顶层模块主要完成 ddr3 读写控制器模块、 FIFO 控制模块和 ddr3 IP 核的例化。 ddr3读写控制器模块负责与 ddr3 IP 核模块的命令和地址的交互,根据 FIFO 控制模块中 fifo 的剩余数据量来切换 DDR3 的读写命令和地址。 ddr3 IP 核模块一边与用户端进行交互,另一边对芯片进行操作,以实现数据的存储。 FIFO 控制模块负责对输入和输出的数据进行时钟域的切换和位宽的转换。

  • //****************************************Copyright (c)***********************************//
  • //原子哥在线教学平台:www.yuanzige.com
  • //技术支持:www.openedv.com
  • //淘宝店铺:http://openedv.taobao.com
  • //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
  • //版权所有,盗版必究。
  • //Copyright(C) 正点原子 2018-2028
  • //All rights reserved
  • //----------------------------------------------------------------------------------------
  • // File name: ddr3_rw_top
  • // Last modified Date: 2020/05/04 9:19:08
  • // Last Version: V1.0
  • // Descriptions: ddr3读写测试顶层模块
  • //
  • //----------------------------------------------------------------------------------------
  • // Created by: 正点原子
  • // Created date: 2019/05/04 9:19:08
  • // Version: V1.0
  • // Descriptions: The original version
  • //
  • //----------------------------------------------------------------------------------------
  • //****************************************************************************************//
  • module ddr3_rw_top(
  • input sys_clk , //系统时钟50M
  • input sys_rst_n , //系统复位
  • output led_error , //读写错误led灯
  • output led_ddr_init_done, //ddr3初始化完成led灯
  • //DDR3接口
  • input pad_loop_in , //低位温度补偿输入
  • input pad_loop_in_h , //高位温度补偿输入
  • output pad_rstn_ch0 , //Memory复位
  • output pad_ddr_clk_w , //Memory差分时钟正端
  • output pad_ddr_clkn_w , //Memory差分时钟负端
  • output pad_csn_ch0 , //Memory片选
  • output [15:0] pad_addr_ch0 , //Memory地址总线
  • inout [16-1:0] pad_dq_ch0 , //数据总线
  • inout [16/8-1:0] pad_dqs_ch0 , //数据时钟正端
  • inout [16/8-1:0] pad_dqsn_ch0 , //数据时钟负端
  • output [16/8-1:0] pad_dm_rdqs_ch0 , //数据Mask
  • output pad_cke_ch0 , //Memory差分时钟使能
  • output pad_odt_ch0 , //On Die Termination
  • output pad_rasn_ch0 , //行地址strobe
  • output pad_casn_ch0 , //列地址strobe
  • output pad_wen_ch0 , //写使能
  • output [2:0] pad_ba_ch0 , //Bank地址总线
  • output pad_loop_out , //低位温度补偿输出
  • output pad_loop_out_h //高位温度补偿输出
  • );
  • //parameter define
  • parameter APP_ADDR_MIN = 28'd0 ; //ddr3读写起始地址,以一个16bit的数据为一个单位
  • //APP_ADDR_MAX = BURST_LENGTH * 8 * (n+1)(n表示突发次数)
  • parameter APP_ADDR_MAX = 28'd5120 ; //ddr3读写结束地址,以一个16bit的数据为一个单位
  • parameter BURST_LENGTH = 8'd64 ; //ddr3读写突发长度,64个128bit的数据
  • parameter DATA_MAX = APP_ADDR_MAX - APP_ADDR_MIN; //读写ddr3的最大数据量
  • //wire define
  • wire [15:0] wr_data ; //DDR3控制器模块写数据
  • wire [15:0] rd_data ; //DDR3控制器模块读数据
  • wire wr_en ; //DDR3控制器模块写使能
  • wire rd_en ; //DDR3控制器模块读使能
  • wire ddr_init_done ; //ddr3初始化完成信号
  • wire error_flag ; //ddr3读写错误标志
  • ////*****************************************************
  • ////** main code
  • ////*****************************************************
  • //ddr3控制器顶层模块
  • ddr3_top u_ddr3_top(
  • .refclk_in (sys_clk ),
  • .rst_n (sys_rst_n ),
  • .app_addr_rd_min (APP_ADDR_MIN ),
  • .app_addr_rd_max (APP_ADDR_MAX ),
  • .rd_bust_len (BURST_LENGTH ),
  • .app_addr_wr_min (APP_ADDR_MIN ),
  • .app_addr_wr_max (APP_ADDR_MAX ),
  • .wr_bust_len (BURST_LENGTH ),
  • .wr_clk (sys_clk ),
  • .rd_clk (sys_clk ),
  • .datain_valid (wr_en ),
  • .datain (wr_data ),
  • .rdata_req (rd_en ),
  • .dataout (rd_data ),
  • .ddr_init_done (ddr_init_done ),
  • //DDR3接口
  • .pad_loop_in (pad_loop_in ),
  • .pad_loop_in_h (pad_loop_in_h ),
  • .pad_rstn_ch0 (pad_rstn_ch0 ),
  • .pad_ddr_clk_w (pad_ddr_clk_w ),
  • .pad_ddr_clkn_w (pad_ddr_clkn_w ),
  • .pad_csn_ch0 (pad_csn_ch0 ),
  • .pad_addr_ch0 (pad_addr_ch0 ),
  • .pad_dq_ch0 (pad_dq_ch0 ),
  • .pad_dqs_ch0 (pad_dqs_ch0 ),
  • .pad_dqsn_ch0 (pad_dqsn_ch0 ),
  • .pad_dm_rdqs_ch0 (pad_dm_rdqs_ch0 ),
  • .pad_cke_ch0 (pad_cke_ch0 ),
  • .pad_odt_ch0 (pad_odt_ch0 ),
  • .pad_rasn_ch0 (pad_rasn_ch0 ),
  • .pad_casn_ch0 (pad_casn_ch0 ),
  • .pad_wen_ch0 (pad_wen_ch0 ),
  • .pad_ba_ch0 (pad_ba_ch0 ),
  • .pad_loop_out (pad_loop_out ),
  • .pad_loop_out_h (pad_loop_out_h )
  • );
  • //ddr3测试数据模块
  • ddr_test u_ddr_test(
  • .clk_50m (sys_clk ), //时钟
  • .rst_n (sys_rst_n ), //复位,低有效
  • .wr_en (wr_en ), //写使能
  • .wr_data (wr_data ), //写数据
  • .rd_en (rd_en ), //读使能
  • .rd_data (rd_data ), //读数据
  • .data_max (DATA_MAX ), //读写ddr的最大数据量
  • .ddr3_init_done(ddr_init_done ), //ddr3初始化完成信号
  • .error_flag (error_flag ) //ddr3读写错误
  • );
  • //利用LED灯指示ddr3读写测试的结果及ddr3是否初始化完成
  • led_disp u_led_disp(
  • .clk_50m (sys_clk ),
  • .rst_n (sys_rst_n ),
  • .ddr3_init_done (ddr_init_done ),
  • .error_flag (error_flag ),
  • .led_error (led_error ),
  • .led_ddr_init_done (led_ddr_init_done)
  • );
  • endmodule

ddr3 读写控制器模块(rw_ctrl_128bit):该模块主要对读写地址操作的信号跳转与读写地址操作。读写操作的状态转换图:

 

    在复位结束后,如果 DDR3 没有初始化完成,那么状态一直在空闲状态(IDLE),否则跳到 DDR3 空闲状态(DDR3_DONE)。

    处理 DDR3 写请求时判断wfifo_rcount,以免写 FIFO 溢出,造成写入 DDR3 的数据丢失。当写 FIFO中的数据量大于一次突发写长度时,执行 DDR3 写地址操作(WRITE_ADDR)。

    处理 DDR3 读请求时判断rfifo_rcount,以免读 FIFO 读空,造成空读现象。当读 FIFO 中的数据量小于一次读突发长度时,执行 DDR3 读地址操作(READ_ADDR)。
    当写地址有效信号和写地址准备信号同时为高时,状态机由写地址状态(WRITE_ADDR) 跳转到写数据状态(WRITE_DATA);当执行完一次突发写长度后,状态机由写数据状态跳转到 DDR3 空闲状态(DDR3_DONE)。

    处理 DDR3 读地址跳转到读数据状态的过程,跳转机制与写状态类似,有别处在于读数据状态(READ_DATA)跳转到 DDR3 空闲状态(DDR3_DONE)的条件是最后一次读信号(axi_rlast)为 1时。
 

  • //****************************************Copyright (c)***********************************//
  • //原子哥在线教学平台:www.yuanzige.com
  • //技术支持:www.openedv.com
  • //淘宝店铺:http://openedv.taobao.com
  • //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
  • //版权所有,盗版必究。
  • //Copyright(C) 正点原子 2018-2028
  • //All rights reserved
  • //----------------------------------------------------------------------------------------
  • // File name: rw_ctrl_128bit
  • // Last modified Date: 2020/05/04 9:19:08
  • // Last Version: V1.0
  • // Descriptions: ddr3读写控制器模块
  • //
  • //----------------------------------------------------------------------------------------
  • // Created by: 正点原子
  • // Created date: 2019/05/04 9:19:08
  • // Version: V1.0
  • // Descriptions: The original version
  • //
  • //----------------------------------------------------------------------------------------
  • //****************************************************************************************//
  • `timescale 1ps/1ps
  • module rw_ctrl_128bit
  • (
  • input clk , //时钟
  • input rst_n , //复位
  • input ddr_init_done , //DDR初始化完成
  • output [32-1:0 ] axi_awaddr , //写地址
  • output reg [7:0 ] axi_awlen , //写突发长度
  • output wire [2:0 ] axi_awsize , //写突发大小
  • output wire [1:0 ] axi_awburst , //写突发类型
  • output axi_awlock , //写锁定类型
  • input axi_awready , //写地址准备信号
  • output reg axi_awvalid , //写地址有效信号
  • output axi_awurgent , //写紧急信号,1:Write address指令优先执行
  • output axi_awpoison , //写抑制信号,1:Write address指令无效
  • output wire [15:0 ] axi_wstrb , //写选通
  • output reg axi_wvalid , //写数据有效信号
  • input axi_wready , //写数据准备信号
  • output reg axi_wlast , //最后一次写信号
  • output wire axi_bready , //写回应准备信号
  • output reg wrfifo_en_ctrl , //写FIFO数据读使能控制位
  • output [32-1:0 ] axi_araddr , //读地址
  • output reg [7:0 ] axi_arlen , //读突发长度
  • output wire [2:0 ] axi_arsize , //读突发大小
  • output wire [1:0 ] axi_arburst , //读突发类型
  • output wire axi_arlock , //读锁定类型
  • output wire axi_arpoison , //读抑制信号,1:Read address指令无效
  • output wire axi_arurgent , //读紧急信号,1:Read address指令优先执行
  • input axi_arready , //读地址准备信号
  • output reg axi_arvalid , //读地址有效信号
  • input axi_rlast , //最后一次读信号
  • input axi_rvalid , //读数据有效信号
  • output wire axi_rready , //读数据准备信号
  • input [10:0 ] wfifo_rcount , //写端口FIFO中的数据量
  • input [10:0 ] rfifo_wcount , //读端口FIFO中的数据量
  • input [27:0 ] app_addr_rd_min , //读DDR3的起始地址
  • input [27:0 ] app_addr_rd_max , //读DDR3的结束地址
  • input [7:0 ] rd_bust_len , //从DDR3中读数据时的突发长度
  • input [27:0 ] app_addr_wr_min , //写DDR3的起始地址
  • input [27:0 ] app_addr_wr_max , //写DDR3的结束地址
  • input [7:0 ] wr_bust_len //从DDR3中写数据时的突发长度
  • );
  • //localparam define
  • localparam IDLE = 4'd1 ; //空闲状态
  • localparam DDR3_DONE = 4'd2 ; //DDR3初始化完成状态
  • localparam WRITE_ADDR = 4'd3 ; //写地址
  • localparam WRITE_DATA = 4'd4 ; //写数据
  • localparam READ_ADDR = 4'd5 ; //读地址
  • localparam READ_DATA = 4'd6 ; //读数据
  • //reg define
  • reg init_start ; //初始化完成信号
  • reg [31:0] init_addr ; //突发长度计数器
  • reg [31:0] axi_araddr_n ; //读地址计数
  • reg [31:0] axi_awaddr_n ; //写地址计数
  • reg [3:0 ] state_cnt ; //状态计数器
  • reg [9:0 ] lenth_cnt ; //突发写次数计数器
  • //wire define
  • wire [9:0 ] lenth_cnt_max; //最大突发次数
  • //*****************************************************
  • //** main code
  • //*****************************************************
  • assign axi_awlock = 1'b0 ;
  • assign axi_awurgent = 1'b0 ;
  • assign axi_awpoison = 1'b0 ;
  • assign axi_bready = 1'b1 ;
  • assign axi_wstrb = {16{1'b1}};
  • assign axi_awsize = 3'b100 ;
  • assign axi_awburst = 2'd1 ;
  • assign axi_arlock = 1'b0 ;
  • assign axi_arurgent = 1'b0 ;
  • assign axi_arpoison = 1'b0 ;
  • assign axi_arsize = 3'b100 ;
  • assign axi_arburst = 2'd1 ;
  • assign axi_rready = 1'b1 ;
  • //计算最大突发次数
  • assign lenth_cnt_max = app_addr_wr_max / (wr_bust_len * 4'd8);
  • //读写地址,因为第0位无效,所以读写地址数据从第1位开始填入
  • assign axi_araddr = {6'b0,axi_araddr_n[24:0],1'b0};
  • assign axi_awaddr = {6'b0,axi_awaddr_n[24:0],1'b0};
  • //稳定ddr3初始化信号
  • always @(posedge clk or negedge rst_n) begin
  • if (!rst_n)
  • init_start <= 1'b0;
  • else if (ddr_init_done)
  • init_start <= ddr_init_done;
  • else
  • init_start <= init_start;
  • end
  • //写地址模块
  • always @(posedge clk or negedge rst_n) begin
  • if (!rst_n) begin
  • axi_awaddr_n <= app_addr_wr_min;
  • axi_awlen <= 8'b0;
  • axi_awvalid <= 1'b0;
  • end
  • //DDR3初始化完成
  • else if (init_start) begin
  • axi_awlen <= wr_bust_len - 1'b1;
  • //当写地址计数小于最后一次写地址起始位时
  • if (axi_awaddr_n < {app_addr_wr_max , 1'b0} - wr_bust_len * 5'd16) begin
  • //写地址有效信号和写地址准备信号都为1时
  • if (axi_awvalid && axi_awready) begin
  • axi_awvalid <= 1'b0; //拉低写地址有效信号
  • //写地址计数加一个突发长度所需的地址
  • axi_awaddr_n <= axi_awaddr_n + wr_bust_len * 5'd16;//wr_bust_len*128/8
  • end
  • //状态机处于写地址状态且写地址准备信号为1时
  • else if (state_cnt == WRITE_ADDR && axi_awready)
  • axi_awvalid <= 1'b1; //拉高写地址有效信号
  • end
  • //当写地址计数等于最后一次写地址起始位时
  • else if (axi_awaddr_n == {app_addr_wr_max , 1'b0} - wr_bust_len * 5'd16) begin
  • if (axi_awvalid && axi_awready) begin
  • axi_awvalid <= 1'b0;
  • axi_awaddr_n <= app_addr_wr_min; //写地址计数清零(回到写起始地址)
  • end
  • else if (state_cnt == WRITE_ADDR && axi_awready)
  • axi_awvalid <= 1'b1;
  • end
  • else
  • axi_awvalid <= 1'b0;
  • end
  • else begin
  • axi_awaddr_n <= axi_awaddr_n;
  • axi_awlen <= 8'b0;
  • axi_awvalid <= 1'b0;
  • end
  • end
  • //写数据模块
  • always @(posedge clk or negedge rst_n) begin
  • if (!rst_n) begin
  • axi_wvalid <= 1'b0 ;
  • axi_wlast <= 1'b0 ;
  • init_addr <= 32'd0 ;
  • lenth_cnt <= 8'd0 ;
  • wrfifo_en_ctrl <= 1'b0;
  • end
  • else begin
  • //DDR3初始化完成
  • if (init_start) begin
  • //当突发写次数计数器小于最大突发次数时
  • if (lenth_cnt < lenth_cnt_max) begin
  • if (axi_wvalid && axi_wready && init_addr < wr_bust_len - 2'd2) begin
  • init_addr <= init_addr + 1'b1;
  • wrfifo_en_ctrl <= 1'b0;
  • end
  • //因为写DDR时已经提前让FIFO准备好第一个数据,所以使能在写结尾要减少一个使能周期
  • else if (axi_wvalid && axi_wready && init_addr == wr_bust_len - 2'd2) begin
  • axi_wlast <= 1'b1;
  • wrfifo_en_ctrl <= 1'b1; //提前一个时钟周期拉高
  • init_addr <= init_addr + 1'b1;
  • end
  • //当突发长度计数器等于一次突发长度时
  • else if (axi_wvalid && axi_wready && init_addr == wr_bust_len - 2'd1) begin
  • axi_wvalid <= 1'b0;
  • axi_wlast <= 1'b0;
  • wrfifo_en_ctrl <= 1'b0;
  • lenth_cnt <= lenth_cnt + 1'b1; //突发写次数计数器加1
  • init_addr <= 32'd0;
  • end
  • else if (state_cnt == WRITE_DATA && axi_wready)
  • axi_wvalid <= 1'b1;
  • else
  • lenth_cnt <= lenth_cnt;
  • end
  • else begin
  • axi_wvalid <= 1'b0 ;
  • axi_wlast <= 1'b0 ;
  • init_addr <= init_addr;
  • lenth_cnt <= 8'd0 ;
  • end
  • end
  • else begin
  • axi_wvalid <= 1'b0 ;
  • axi_wlast <= 1'b0 ;
  • init_addr <= 32'd0;
  • lenth_cnt <= 8'd0 ;
  • end
  • end
  • end
  • //读地址模块
  • always @(posedge clk or negedge rst_n) begin
  • if (!rst_n) begin
  • axi_araddr_n <= app_addr_rd_min;
  • axi_arlen <= 8'b0;
  • axi_arvalid <= 1'b0;
  • end
  • //DDR3初始化完成
  • else if(init_start) begin
  • axi_arlen <= rd_bust_len - 1'b1;
  • //当读地址计数小于最后一次读地址起始位时
  • if (axi_araddr_n < {app_addr_rd_max , 1'b0} - rd_bust_len * 5'd16) begin
  • if (axi_arready && axi_arvalid) begin
  • axi_arvalid <= 1'b0;
  • axi_araddr_n <= axi_araddr_n + rd_bust_len * 5'd16;
  • end
  • else if(axi_arready && state_cnt == READ_ADDR)
  • axi_arvalid <= 1'b1;
  • end
  • //当读地址计数等于最后一次读地址起始位时
  • else if (axi_araddr_n == {app_addr_rd_max , 1'b0} - rd_bust_len * 5'd16) begin
  • if (axi_arready && axi_arvalid) begin
  • axi_arvalid <= 1'b0;
  • axi_araddr_n <= app_addr_rd_min;
  • end
  • else if(axi_arready && state_cnt==READ_ADDR)
  • axi_arvalid <= 1'b1;
  • end
  • else
  • axi_arvalid <= 1'b0;
  • end
  • else begin
  • axi_araddr_n <= app_addr_rd_min;
  • axi_arlen <= 8'b0;
  • axi_arvalid <= 1'b0;
  • end
  • end
  • //DDR3读写逻辑实现模块
  • always @(posedge clk or negedge rst_n) begin
  • if(~rst_n) begin
  • state_cnt <= IDLE;
  • end
  • else begin
  • case(state_cnt)
  • IDLE:begin
  • if(init_start)
  • state_cnt <= DDR3_DONE ;
  • else
  • state_cnt <= IDLE;
  • end
  • DDR3_DONE:begin
  • if(wfifo_rcount >= wr_bust_len)
  • state_cnt <= WRITE_ADDR; //跳到写地址操作
  • else if(rfifo_wcount < rd_bust_len)
  • state_cnt <= READ_ADDR; //跳到读地址操作
  • else
  • state_cnt <= state_cnt;
  • end
  • WRITE_ADDR:begin
  • if(axi_awvalid && axi_awready)
  • state_cnt <= WRITE_DATA; //跳到写数据操作
  • else
  • state_cnt <= state_cnt; //条件不满足,保持当前值
  • end
  • WRITE_DATA:begin
  • if(axi_wvalid && axi_wready && init_addr == wr_bust_len - 1)
  • state_cnt <= DDR3_DONE; //写到设定的长度跳到等待状态
  • else
  • state_cnt <= state_cnt; //写条件不满足,保持当前值
  • end
  • READ_ADDR:begin
  • if(axi_arvalid && axi_arready)
  • state_cnt <= READ_DATA; //跳到写数据操作
  • else
  • state_cnt <= state_cnt; //条件不满足,保持当前值
  • end
  • READ_DATA:begin
  • if(axi_rlast) //读到设定的地址长度
  • state_cnt <= DDR3_DONE; //则跳到空闲状态
  • else
  • state_cnt <= state_cnt; //否则保持当前值
  • end
  • default:begin
  • state_cnt <= IDLE;
  • end
  • endcase
  • end
  • end
  • endmodule

    

    ddr3 控制器 fifo 控制模块(ddr3_fifo_ctrl):该模块例化了两个 FIFO IP 核,分别为 128 位进 16 位出的读 FIFO 和 16 位进 128 位出的写 FIFO。读FIFO 是将 DDR3 输出的 128 位宽的数据转为 16 位宽的数据后输出给用户;写 FIFO 是将用户输入的 16 位宽的数据转为 128 位宽的数据后输出给 DDR3。

  • //****************************************Copyright (c)***********************************//
  • //原子哥在线教学平台:www.yuanzige.com
  • //技术支持:www.openedv.com
  • //淘宝店铺:http://openedv.taobao.com
  • //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
  • //版权所有,盗版必究。
  • //Copyright(C) 正点原子 2018-2028
  • //All rights reserved
  • //----------------------------------------------------------------------------------------
  • // File name: ddr3_fifo_ctrl
  • // Last modified Date: 2020/05/04 9:19:08
  • // Last Version: V1.0
  • // Descriptions: ddr3控制器fifo控制模块
  • //
  • //----------------------------------------------------------------------------------------
  • // Created by: 正点原子
  • // Created date: 2019/05/04 9:19:08
  • // Version: V1.0
  • // Descriptions: The original version
  • //
  • //----------------------------------------------------------------------------------------
  • //****************************************************************************************//
  • `timescale 1ns / 1ps
  • module ddr3_fifo_ctrl(
  • input rst_n , //复位信号
  • input wr_clk , //wfifo时钟
  • input rd_clk , //rfifo时钟
  • input clk_100 , //用户时钟
  • input datain_valid , //数据有效使能信号
  • input [15:0] datain , //有效数据
  • input [127:0] rfifo_din , //用户读数据
  • input rdata_req , //请求像素点颜色数据输入
  • input rfifo_wren , //从ddr3读出数据的有效使能
  • input wfifo_rden , //wfifo读使能
  • output [127:0] wfifo_dout , //用户写数据
  • output [10:0] wfifo_rcount , //rfifo剩余数据计数
  • output [10:0] rfifo_wcount , //wfifo写进数据计数
  • output [15:0] pic_data //有效数据
  • );
  • rd_fifo u_rd_fifo (
  • .wr_clk (clk_100 ), // input
  • .wr_rst (~rst_n ), // input
  • .wr_en (rfifo_wren ), // input
  • .wr_data (rfifo_din ), // input [127:0]
  • .wr_full ( ), // output
  • .wr_water_level (rfifo_wcount), // output
  • .almost_full ( ), // output
  • .rd_clk (rd_clk ), // input
  • .rd_rst (~rst_n ), // input
  • .rd_en (rdata_req ),
  • .rd_data (pic_data ), // output [15:0]
  • .rd_empty ( ), // output
  • .rd_water_level ( ), // output
  • .almost_empty ( ) // output
  • );
  • wr_fifo u_wr_fifo (
  • .wr_clk (wr_clk ), // input
  • .wr_rst (~rst_n ), // input
  • .wr_en (datain_valid),
  • .wr_data (datain ), //input [15:0]
  • .wr_full ( ), // output
  • .wr_water_level ( ), // output
  • .almost_full ( ), // output
  • .rd_clk (clk_100 ), // input
  • .rd_rst (~rst_n ), // input
  • .rd_en (wfifo_rden ), // input
  • .rd_data (wfifo_dout ), // output [127:0]
  • .rd_empty ( ), // output
  • .rd_water_level (wfifo_rcount), // output
  • .almost_empty ( ) // output
  • );
  • endmodule

    ddr 测试数据模块(ddr_test):ddr 测试数据模块从起始地址开始,连续向 5120 个存储空间中写入数据 0~5119。写完成后一直进行读操作,持续将该存储空间的数据读出。
 

  • //****************************************Copyright (c)***********************************//
  • //原子哥在线教学平台:www.yuanzige.com
  • //技术支持:www.openedv.com
  • //淘宝店铺:http://openedv.taobao.com
  • //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
  • //版权所有,盗版必究。
  • //Copyright(C) 正点原子 2018-2028
  • //All rights reserved
  • //----------------------------------------------------------------------------------------
  • // File name: ddr_test
  • // Last modified Date: 2020/09/04 9:19:08
  • // Last Version: V1.0
  • // Descriptions: ddr测试数据模块
  • //
  • //----------------------------------------------------------------------------------------
  • // Created by: 正点原子
  • // Created date: 2020/09/04 9:19:08
  • // Version: V1.0
  • // Descriptions: The original version
  • //
  • //----------------------------------------------------------------------------------------
  • //****************************************************************************************//
  • module ddr_test(
  • input clk_50m , //时钟
  • input rst_n , //复位,低有效
  • output reg wr_en , //写使能
  • output reg [15:0] wr_data , //写数据
  • output reg rd_en , //读使能
  • input [15:0] rd_data , //读数据
  • input [27:0] data_max , //写入ddr的最大数据量
  • input ddr3_init_done, //ddr3初始化完成信号
  • output reg error_flag //ddr3读写错误
  • );
  • //reg define
  • reg init_done_d0;
  • reg init_done_d1;
  • reg [27:0] wr_cnt ; //写操作计数器
  • reg [27:0] rd_cnt ; //读操作计数器
  • reg rd_valid ; //读数据有效标志
  • reg [27:0] rd_cnt_d0 ;
  • //*****************************************************
  • //** main code
  • //*****************************************************
  • //同步ddr3初始化完成信号
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n) begin
  • init_done_d0 <= 1'b0 ;
  • init_done_d1 <= 1'b0 ;
  • end
  • else begin
  • init_done_d0 <= ddr3_init_done;
  • init_done_d1 <= init_done_d0;
  • end
  • end
  • //对读计数器做一拍延时使数据对齐
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • rd_cnt_d0 <= 28'd0;
  • else
  • rd_cnt_d0 <= rd_cnt;
  • end
  • //ddr3初始化完成之后,写操作计数器开始计数
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • wr_cnt <= 28'd0;
  • else if(init_done_d1 && (wr_cnt < data_max ))
  • wr_cnt <= wr_cnt + 1'b1;
  • else
  • wr_cnt <= wr_cnt;
  • end
  • //ddr3写端口FIFO的写使能、写数据
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n) begin
  • wr_en <= 1'b0;
  • wr_data <= 16'd0;
  • end
  • else if(wr_cnt >= 11'd0 && (wr_cnt < data_max )&&init_done_d1) begin
  • wr_en <= 1'b1; //写使能拉高
  • wr_data <= wr_cnt[15:0]; //写入数据
  • end
  • else begin
  • wr_en <= 1'b0;
  • wr_data <= 16'd0;
  • end
  • end
  • //写入数据完成后,开始读操作
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • rd_en <= 1'b0;
  • else if(wr_cnt >= data_max ) //写数据完成
  • rd_en <= 1'b1; //读使能
  • else
  • rd_en <= rd_en;
  • end
  • //对读操作计数
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • rd_cnt <= 28'd0;
  • else if(rd_en) begin
  • if(rd_cnt < data_max - 1'd1)
  • rd_cnt <= rd_cnt + 1'd1;
  • else
  • rd_cnt <= 28'd0;
  • end
  • end
  • //第一次读取的数据无效,后续读操作所读取的数据才有效
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • rd_valid <= 1'b0;
  • else if(rd_cnt >= data_max - 1'd1 ) //等待第一次读操作结束
  • rd_valid <= 1'b1; //后续读取的数据有效
  • else
  • rd_valid <= rd_valid;
  • end
  • //读数据有效时,若读取数据错误,给出标志信号
  • always @(posedge clk_50m or negedge rst_n) begin
  • if(!rst_n)
  • error_flag <= 1'b0;
  • else if(wr_en)
  • error_flag <= 1'b0;
  • else if(rd_valid && ((rd_data[15:0] != rd_cnt_d0[15:0])) )
  • error_flag <= 1'b1; //若读取的数据错误,将错误标志位拉高
  • else
  • error_flag <= error_flag;
  • end
  • endmodule

    LED 显示模块(led_display):LED 显示模块用 LED 不同的显示状态指示 ddr3 初始完成情况(LED0 常亮表示 ddr3 初始化完成)和ddr3 读写测试的结果:若读写测试正确无误,则 LED1 常亮;若出现错误(读出的数据与写入的数据不一致),则 LED1 以 0.5s 为周期闪烁。

 

5、下载验证

    LED0 在短暂延时之后,开始处于常亮的状态表明 DDR3 初始化完成,若 LED1 保持常亮说明读数据正确, DDR3 读写测试实验验证成功:

 

 

查看本帖全部内容,请登录或者注册
点赞(1) 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
有奖直播 | AI之眼——安森美图像传感器 报名中
直播时间:2025年4月25日(周五)上午10:00-11:30
直播主题:AI之眼——安森美图像传感器
报名观看直播、直播间提问、填写问卷均有机会获得精美礼品!

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 3

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表