Altera DDR2 IP核学习总结3-----------DDR2 IP核的使用
Posted librarian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_W1‘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核的使用的主要内容,如果未能解决你的问题,请参考以下文章
小梅哥FPGA进阶学习之旅基于Altera FPGA 的DDR2+千兆以太网电路设计
采用激励driver模块IP核模拟DDR2控制器的用户侧,来调度DDR2控制器。 请问你的激励driver模块IP核是哪里来