根據上一篇生成的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驅動成功。