Altera DDR2 IP核学习总结3-----------DDR2 IP核的使用

根据上一篇生成的IP核,例化之后如上图,Local开头的数据是用户侧数据,其他数据暂时不用纠结,不用管。

这些是需要关注的信号,但是初学阶段很难对这些信号形成具体的概念,这里参考明德扬的代码进行二次封装。
- module ddr2_intf(
- clk_in ,
- clk_out ,
- rst_n ,
- local_address ,
- local_write_req ,
- local_read_req ,
- local_wdata ,
- local_ready ,
- local_rdata ,
- local_rdata_valid,
- local_init_done ,
- local_burstbegin ,
- user_wdata ,
- user_wdata_en ,
- user_waddr ,
- user_raddr ,
- user_rdata_en ,
- user_rdata ,
- user_rdata_vld ,
- user_wdata_rdy ,
- user_rdata_rdy
- );
- parameter ADDR_W = 23 ;
- parameter DDR_DATA_W = 32 ;
- parameter BURST = 2 ;
- parameter USE_DATA_W = DDR_DATA_W * BURST;
- parameter FIFO_W = USE_DATA_W + ADDR_W;
- input clk_in ;
- input rst_n ;
- input clk_out ;
- input local_ready ;
- input [DDR_DATA_W-1:0] local_rdata ;
- input local_rdata_valid;
- input local_init_done ;
- input [USE_DATA_W-1:0] user_wdata ;
- input user_wdata_en ;
- input [ADDR_W-1:0] user_waddr ;
- input [ADDR_W-1:0] user_raddr ;
- input user_rdata_en ;
- output [ADDR_W-1:0] local_address ;
- output local_write_req ;
- output local_read_req ;
- output [DDR_DATA_W-1:0] local_wdata ;
- output local_burstbegin ;
- output [USE_DATA_W-1:0] user_rdata ;
- output user_rdata_vld ;
- output user_wdata_rdy ;
- output user_rdata_rdy ;
- reg [USE_DATA_W-1:0] user_rdata ;
- reg user_rdata_vld ;
- reg user_wdata_rdy ;
- reg user_rdata_rdy ;
- wire [ADDR_W-1:0] local_address ;
- wire local_write_req ;
- wire local_read_req ;
- wire [DDR_DATA_W-1:0] local_wdata ;
- wire local_burstbegin ;
- wire [FIFO_W-1:0] wfifo_wdata ;
- wire wfifo_wrreq ;
- wire wfifo_empty ;
- wire wfifo_rdreq ;
- wire [FIFO_W-1:0] wfifo_q ;
- wire [ 5:0] wfifo_usedw ;
- wire [ADDR_W-1:0] rfifo_wdata ;
- wire rfifo_wrreq ;
- wire rfifo_empty ;
- wire rfifo_rdreq ;
- wire [ADDR_W-1:0] rfifo_q ;
- wire [ 5:0] rfifo_usedw ;
- reg [FIFO_W :0] ififo_wdata ;
- reg ififo_wrreq ;
- wire ififo_empty ;
- wire ififo_rdreq ;
- wire [FIFO_W :0] ififo_q ;
- wire [ 5:0] ififo_usedw ;
- wire ififo_rdy ;
- reg [USE_DATA_W-1:0] gfifo_wdata ;
- reg gfifo_wrreq ;
- wire gfifo_empty ;
- wire gfifo_rdreq ;
- wire [USE_DATA_W-1:0] gfifo_q ;
- wire [ 5:0] gfifo_usedw ;
- reg [ 1:0] cnt0 ;
- wire add_cnt0 ;
- wire end_cnt0 ;
- reg [ 2:0] x ;
- reg [ 3:0] cnt1 ;
- wire add_cnt1 ;
- wire end_cnt1 ;
- fifo_ahead_sys #(.DATA_W(FIFO_W),.DEPT_W(64)) u_wfifo(
- .aclr (~rst_n ),
- .clock (clk_in ),
- .data (wfifo_wdata ),
- .rdreq (wfifo_rdreq ),
- .wrreq (wfifo_wrreq ),
- .empty (wfifo_empty ),
- .q (wfifo_q ),
- .usedw (wfifo_usedw )
- );
- fifo_ahead_sys #(.DATA_W(ADDR_W),.DEPT_W(64)) u_rfifo(
- .aclr (~rst_n ),
- .clock (clk_in ),
- .data (rfifo_wdata ),
- .rdreq (rfifo_rdreq ),
- .wrreq (rfifo_wrreq ),
- .empty (rfifo_empty ),
- .q (rfifo_q ),
- .usedw (rfifo_usedw )
- );
- fifo_ahead_asy #(.DATA_W(FIFO_W+1),.DEPT_W(64)) u_ififo(
- .aclr (~rst_n ),
- .data (ififo_wdata ),
- .rdclk (clk_out ),
- .rdreq (ififo_rdreq ),
- .wrclk (clk_in ),
- .wrreq (ififo_wrreq ),
- .q (ififo_q ),
- .rdempty (ififo_empty ),
- .wrusedw (ififo_usedw )
- );
- assign wfifo_wdata = {user_waddr,user_wdata};
- assign wfifo_wrreq = user_wdata_en ;
- assign wfifo_rdreq = ififo_rdy && wfifo_empty==0 && rfifo_empty==1;
- assign rfifo_wdata = user_raddr ;
- assign rfifo_wrreq = user_rdata_en ;
- assign rfifo_rdreq = ififo_rdy && rfifo_empty==0;
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- ififo_wdata <= 0;
- end
- else if(wfifo_rdreq) begin
- ififo_wdata <= {1'b1,wfifo_q};
- end
- else if(rfifo_rdreq)begin
- ififo_wdata <= {1'b0,rfifo_q,{USE_DATA_W{1'b0}}};
- end
- end
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- ififo_wrreq <= 0;
- end
- else begin
- ififo_wrreq <= wfifo_rdreq || rfifo_rdreq;
- end
- end
- assign ififo_rdy = ififo_usedw<40;
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- user_wdata_rdy <= 0;
- end
- else begin
- user_wdata_rdy <= wfifo_usedw<40;
- end
- end
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- user_rdata_rdy <= 0;
- end
- else begin
- user_rdata_rdy <= rfifo_usedw<40;
- end
- end
- wire local_read_req_tmp;
- assign local_wdata = ififo_q[USE_DATA_W-DDR_DATA_W*cnt0-1 -:DDR_DATA_W];
- assign local_address = ififo_q[FIFO_W-1 -:ADDR_W];
- assign local_write_req = ififo_q[FIFO_W] && ififo_empty==0 && local_init_done;
- assign local_read_req_tmp = ififo_q[FIFO_W]==0 && ififo_empty==0 && local_init_done;
- assign local_read_req = local_read_req_tmp && cnt0==1-1;
- assign local_burstbegin= add_cnt0 && cnt0==1-1;
- assign ififo_rdreq = end_cnt0;
- reg ififo_q_ff0;
- always @(posedge clk_out or negedge rst_n)begin
- if(rst_n==1'b0)begin
- ififo_q_ff0<=0;
- end
- else begin
- ififo_q_ff0<=ififo_q[FIFO_W];
- end
- end
- always @(posedge clk_out or negedge rst_n)begin
- if(!rst_n)begin
- cnt0 <= 0;
- end
- else if(add_cnt0)begin
- if(end_cnt0)
- cnt0 <= 0;
- else
- cnt0 <= cnt0 + 1;
- end
- end
- assign add_cnt0 = (local_write_req||local_read_req_tmp)&&local_ready;
- assign end_cnt0 = add_cnt0 && cnt0==2-1;
- always @(posedge clk_out or negedge rst_n)begin
- if(!rst_n)begin
- cnt1 <= 0;
- end
- else if(add_cnt1)begin
- if(end_cnt1)
- cnt1 <= 0;
- else
- cnt1 <= cnt1 + 1;
- end
- end
- assign add_cnt1 = local_rdata_valid;
- assign end_cnt1 = add_cnt1 && cnt1==BURST-1 ;
- always @(posedge clk_out or negedge rst_n)begin
- if(rst_n==1'b0)begin
- gfifo_wrreq <= 0;
- end
- else begin
- gfifo_wrreq <= end_cnt1;
- end
- end
- always @(posedge clk_out or negedge rst_n)begin
- if(rst_n==1'b0)begin
- gfifo_wdata <= 0;
- end
- else if(add_cnt1)begin
- gfifo_wdata[USE_DATA_W - DDR_DATA_W*cnt1 -1 -:DDR_DATA_W] <= local_rdata;
- end
- end
- fifo_ahead_asy #(.DATA_W(USE_DATA_W),.DEPT_W(64)) u_gfifo(
- .aclr (~rst_n ),
- .data (gfifo_wdata ),
- .rdclk (clk_in ),
- .rdreq (gfifo_rdreq ),
- .wrclk (clk_out ),
- .wrreq (gfifo_wrreq ),
- .q (gfifo_q ),
- .rdempty (gfifo_empty ),
- .wrusedw (gfifo_usedw )
- );
- assign gfifo_rdreq = gfifo_empty==0;
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- user_rdata <= 0;
- end
- else begin
- user_rdata <= gfifo_q;
- end
- end
- always @(posedge clk_in or negedge rst_n)begin
- if(rst_n==1'b0)begin
- user_rdata_vld <= 0;
- end
- else begin
- user_rdata_vld <= gfifo_rdreq;
- end
- end
- endmodule
- ddr2_intf u8(
- .clk_in ( clk ),
- .clk_out ( phy_clk ),
- .rst_n ( rst_n_ff1 ),
- .local_address ( local_address ),
- .local_write_req ( local_write_req ),
- .local_read_req ( local_read_req ),
- .local_wdata ( local_wdata ),
- .local_ready ( local_ready ),
- .local_rdata ( local_rdata ),
- .local_rdata_valid( local_rdata_valid ),
- .local_init_done ( local_init_done ),
- .local_burstbegin ( local_burstbegin ),
- .user_wdata ( user_wdata ),
- .user_wdata_en ( user_wdata_en ),
- .user_waddr ( user_waddr ),
- .user_raddr ( user_raddr ),
- .user_rdata_en ( user_rdata_en ),
- .user_rdata ( user_rdata ),
- .user_rdata_vld ( user_rdata_vld ),
- .user_wdata_rdy ( user_wdata_rdy ),
- .user_rdata_rdy ( user_rdata_rdy )
- );
封装之后只需要关注
- .user_wdata ( user_wdata ),
- .user_wdata_en ( user_wdata_en ),
- .user_waddr ( user_waddr ),
- .user_raddr ( user_raddr ),
- .user_rdata_en ( user_rdata_en ),
- .user_rdata ( user_rdata ),
- .user_rdata_vld ( user_rdata_vld ),
- .user_wdata_rdy ( user_wdata_rdy ),
- .user_rdata_rdy ( user_rdata_rdy )
上面这9个信号,当user_wdata_rdy为高电平的时候可以写入数据,user_rdata_rdy ( user_rdata_rdy ) 可以读出数据,
写了一个简单的测试程序
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt0 <= 0;
- end
- else if(add_cnt0)begin
- if(end_cnt0)
- cnt0 <= 0;
- else
- cnt0 <= cnt0 + 1;
- end
- end
- assign add_cnt0 = user_wdata_en ;
- assign end_cnt0 = add_cnt0 && cnt0==100 ;
- assign user_wdata_en = user_wdata_rdy && flag_add==0;
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt1 <= 0;
- end
- else if(add_cnt1)begin
- if(end_cnt1)
- cnt1 <= 0;
- else
- cnt1 <= cnt1 + 1;
- end
- end
- assign add_cnt1 = user_rdata_en ;
- assign end_cnt1 = add_cnt1 && cnt1==100 ;
- assign user_rdata_en = user_rdata_rdy && flag_add==1;
- always @(posedge clk)begin
- if(!rst_n)
- flag_add <= 0;
- else if(end_cnt0)
- flag_add <= 1;
- else if(end_cnt1)
- flag_add <= 0;
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- user_wdata <= 0;
- user_waddr <= 0;
- user_raddr <= 0;
- end
- else begin
- user_wdata <= cnt0 ;
- user_waddr <= cnt0*2 ;
- user_raddr <= cnt1*2 ;
- end
- end
当flag_add为低电平的时候往DDR2中写入数据,flag_add为高电平的时候读出数据。一共写入一百个数据。
使用signaltapII抓到的波形为

local_init_done为高电平说明DDR2芯片初始化完成,user_wdata_en为高电平时写入数据,放大了看则是

写入的数据为地址的二分之一
然后看一下读出来的数据

这里很清楚的可以看出来user_rdata_en跟user_rdata_vld并不能对齐
放大了看

当user_rdata_vld为高电平的时候输出的数据有效,

测量一下得知user_rdata_en跟user_rdata_vld的偏差是21个数据,从Altera DDR2 IP核学习总结1中得知DRAM数据输出有延迟,结构相同的DDR2自然有相同的特性,大神称为首字惩罚特性(当时纠结这个问题半天,怎么也解决不了,多谢群里的大神)。
观察写入数据和读取数据一致,DDR2驱动成功。
Altera DDR2 IP核学习总结3-----------DDR2 IP核的使用的更多相关文章
- 关于AXI4-Stream to Video Out 和 Video Timing Controller IP核学习
关于AXI4-Stream to Video Out 和 Video Timing Controller IP核学习 1.AXI4‐Stream to Video Out Top‐Level Sign ...
- xilinx AXI相关IP核学习
xilinx AXI相关IP核学习 1.阅读PG044 (1)AXI4‐Stream to Video Out Top‐Level Signaling Interface (2)AXI4‐Stream ...
- TCP/IP协议学习(五) 基于C# Socket的C/S模型
TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的:然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多.C#通过提供的Socket API ...
- linux 学习 设置固定网Ip
本人使用CentOs6.5 最近在学习linux操作系统,单在使用shell连接前都要使用ifconfig eth0 设置一个临时IP让我不胜其烦.决定学习设置一个固定IP 步骤: 1.登录计算机后使 ...
- Lattice 的 Framebuffer IP核使用调试笔记之IP核生成与参数设置
本文由远航路上ing 原创,转载请标明出处. 这节笔记记录IP核的生成以及参数设置. 先再IP库里下载安装Framebuffer 的ipcore 并安装完毕. 一.IP核的生成: 1.先点击IP核则右 ...
- TCP/IP协议学习笔记
计算机网络基础知识复习汇总:计算机网络基础知识复习 HTTP协议的解析:剖析 HTTP 协议 一个系列的解析文章: TCP/IP详解学习笔记(1)-- 概述 TCP/IP详解学习笔记(2)-- 数据链 ...
- TCP/IP协议学习之实例ping命令学习笔记
TCP/IP协议学习之实例ping命令学习笔记(一) 一. 目的为了让网络协议学习更有效果,在真实网络上进行ping命令前相关知识的学习,暂时不管DNS,在内网中,进行2台主机间的ping命令的整个详 ...
- IP地址和子网划分学习笔记之《IP地址详解》
2018-05-03 18:47:37 在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. ...
- 【计算机网络】网络层学习笔记:总结IP,NAT和DHCP
前言:这篇文章是学习网络层协议时候总结的笔记,前面的主要部分介绍的都是IP协议, 后半部分介绍NAT协议和DHCP协议 参考书籍 <计算机网络-自顶向下> 作者 James F ...
随机推荐
- node + mongoDB
在MongoDB安装这篇博客中已经创建了一个bella_blog的数据库,该数据已经包含了user集合. 下面就可以在node sever端用MongoDB了. Mongoose库简而言之就是在nod ...
- Java 工厂方法模式的简单示例
工厂方法模式:也叫工厂模式,属于类创建型模式,工厂父类(接口)负责定义产品对象的公共接口,而子类工厂则负责创建具体的产品对象. 目的:是为了把产品的实例化操作延迟到子类工厂中完成,通过工厂子类来决定究 ...
- 多对多第三张表的创建方式 和 forms组件的使用
目录 一.多对多第三张表的创建 1. 全自动方式 (1)实现代码 (2)优点和不足 2. 纯手撸方式(了解) (1)实现代码 (2)优点和不足 3. 半自动方式(推荐使用) (1)实现代码 (2)优点 ...
- 【SQL-自动生成编号】按规则自动生成单据编号 以及并发问题_使用触发器、函数 等
描述:每种单据新建时,自动生成它的单据编号. 规则如:固定码+日期+流水号 (ABC1603180001) 方法一:触发器 触发器的缺点是,执行了sql之后才看到编码. 测试:流水号不能超过最大数,否 ...
- Mybatis中通过父类/接口来限定类的别名(TypeAlias)配置
- SPP框架的基本使用
入职两天 Day1.Day2: 学习SPP框架 SPP是什么? SPP提供了一系列的基础功能,是一个通用的网络服务器运行框架.主要由proxy,worker,controller三个模块组成.它提供A ...
- DIY Arduino 方向盘
之前的项目中使用Arduino做UE4的输入设备时候需要用到UE4Duino这个插件,以字符串的形式从Arduino中组装信息并发送到串口,使用UE4Duino进行解析,过程比较麻烦. 最近发现的一个 ...
- Exchanger 原理
Exchanger(交换者)是一个用于线程间协作的工具类.Exchanger用于进行线程间的数据交换.它提供一个同步点,在这个同步点两个线程可以交换彼此的数据.这两个线程通过exchange方法交换数 ...
- 微信小程序 — 速学速查笔记
1. 配置 配置全解析 project.config.json ( 项目配置文件 ) { // 文件描述 "description": "项目配置文件", // ...
- vue router 如何使用params query传参,以及有什么区别
写在前面: 传参是前端经常需要用的一个操作,很多场景都会需要用到上个页面的参数,本文将会详细介绍vue router 是如何进行传参的,以及一些小细节问题.有需要的朋友可以做一下参考,喜欢的可以点波赞 ...