用户端实现DDR3 SDRAM写、读控制

一、项目目的

  用户端的写、读控制主要功能需要按照FIFO IP核能够接受的逻辑,编写对应的逻辑功能,因此该控制器的输出应该按照FIFO IP核的时序进行。

  1、用户写控制器实现

  下面先对写控制器进行分析。FIFO IP核预留的写端口主要包括两个FIFO接口,其中一个可以缓存数据,另外一个可以缓存命令。为了保证数据在写入DDR3 SDRAM中时,不出线数据量和突发长度不一致的情况,我们可以先将需要写入的数据存到数据FIFO中,在对命令FIFO发送写命令相关的信息,这样就可以保证FIFO IP核接受搭配命令之后,有正确的突发长度的数据可以传输。

  2、用户写控制器框架

  由上图可以看到,需要写入的数据wr_data由wr_en标志有效,我们需要在该模块实现由输入的wr_data和wr_en产生FIFO IP核写端口所需要的信息,并最终有FIFO  IP核写数据端口的FIFO标志产生用户端写完成信号。

3、 该模块的各个变量说明

 

 

4、用户端写控制的时序图

   由上图可以看到,我们根据输入进来的数据的数量可以确定突发的长度,当输入的数据结束时,产生命令FIFO的写使能等端口信息。当FIFO IP核在接受到命令FIFO的命令后,会将数据FIFO取出写入到DDR3 SDRAM中,我们可以根据FIFO的空标志确定是否完成该过程,当出现数据FIFO的空标志上升沿,则认为所有突发的空标志均可以写入到DDR3 SDRAM中,此时我们可以产生一个user_wr_end信号告知外部模块本次突发写结束。

  地址的计算:DDR3是16位的数据地址,每个地址递增1代表着写入16位的数据,呢么128位数据时8个16位的DDR存储空间。

代码:

`timescale 1ns / 1ps
//**************************************************************************
// *** 名称 : user_wr_ctrl.v
// *** 作者 : tedyma
// *** 博客 : https://www.cnblogs.com/tedymafpga/
// *** 日期 : 2019-08-10
// *** 描述 : 用户写逻辑,只需要控制少量的接口来控制更多的命令
//**************************************************************************
module user_wr_ctrl
//========================< 端口 >==========================================
(
input   wire                sclk                ,
input   wire                rst                 ,
input   wire                wr_en               ,
input   wire    [127:0]     wr_data             ,
output  wire                p2_wr_en            ,
output  wire    [127:0]     p2_wr_data          ,
output  wire    [15:0]      p2_wr_mask          ,
output  wire                p2_cmd_en           ,
output  wire    [6:0]       p2_cmd_bl           ,
output  wire    [2:0]       p2_cmd_instr        ,
output  wire    [27:0]      p2_cmd_addr         ,
output  wire                user_wr_end         ,
input   wire                p2_wr_empty
);
//--------------------------------------------------------------------------
//--    参数化
//--------------------------------------------------------------------------
parameter       BURST_LEN = 64                  ; //突发长度
parameter       START_ADDR = 0                  ; //开始地址
parameter       STOP_ADDR = 785920              ; //1024*768-512(我们这里设置分辨率为1024*768)512=64*128/16
parameter       ADDR_ADD = 64*128/16            ; //每次需要增加的地址
//--------------------------------------------------------------------------
//--    信号定义
//--------------------------------------------------------------------------
reg                         wr_en_r             ;
reg     [127:0]             wr_data_r           ;
reg                         p2_cmd_en_r         ;
reg     [27:0]              p2_cmd_addr_r       ;
reg     [6:0]               data_cnt            ;
reg             p2_wr_empty_r0,p2_wr_empty_r1,p2_wr_empty_r2;
reg                         user_wr_end_r       ;
//--------------------------------------------------------------------------
//--    输出的接口信号
//--------------------------------------------------------------------------
assign  p2_wr_data      =   wr_data_r           ;
assign  p2_wr_en        =   wr_en_r             ;
assign  p2_cmd_en       =   p2_cmd_en_r         ;
assign  p2_wr_mask      =   0                   ;
assign  p2_cmd_bl       =   BURST_LEN           ;   
assign  p2_cmd_instr    =   3'd0                ;
assign  p2_cmd_addr     =   p2_cmd_addr_r       ;   
assign  user_wr_end     =   user_wr_end_r       ;
//--------------------------------------------------------------------------
//--   数据和写命令
//--------------------------------------------------------------------------
always @(posedge sclk) begin
    if(rst) begin
        wr_en_r <= 1'b0;
        wr_data_r <= 'd0;
    end
    else begin
        wr_en_r <= wr_en;
        wr_data_r <= wr_data;
    end
end
//--------------------------------------------------------------------------
//--    wr_cmd_en命令
//--------------------------------------------------------------------------
always @(posedge sclk) begin
    if(rst) begin
        p2_cmd_en_r <= 1'b0;
    end
    else if(wr_en == 1'b0 && wr_en_r == 1'b1)begin
        p2_cmd_en_r <= 1'b1;
    end
    else begin
        p2_cmd_en_r <= 1'b0;
    end
end
//--------------------------------------------------------------------------
//-- 地址   
//--------------------------------------------------------------------------
always @(posedge sclk) begin
    if(rst) begin
        p2_cmd_addr_r <= START_ADDR;
    end
    else if(p2_cmd_addr_r == STOP_ADDR && p2_cmd_en_r == 1'b1)begin
        p2_cmd_addr_r <= START_ADDR;
    end
    else if(p2_cmd_en_r == 1'b1)begin
        p2_cmd_addr_r <= p2_cmd_addr_r + ADDR_ADD;
    end
end
//--------------------------------------------------------------------------
//--  对输入的FIFO空信号打两拍(在top顶层模块,也需要打两拍,作用不一样)
//--------------------------------------------------------------------------
always @(posedge sclk)begin
    p2_wr_empty_r0 <= p2_wr_empty;
    p2_wr_empty_r1 <= p2_wr_empty_r0;
    p2_wr_empty_r2 <= p2_wr_empty_r1;
end
//--------------------------------------------------------------------------
//--    用户结束信号
//--------------------------------------------------------------------------
always @(posedge sclk) begin
    if(rst) begin
        user_wr_end_r <= 1'b0;
    end
    else if(p2_wr_empty_r1 == 1'b1 && p2_wr_empty_r2 == 1'b0)begin
        user_wr_end_r <= 1'b1;
    end
    else begin
        user_wr_end_r <= 1'b0;
    end
end
endmodule

二、用户端的读实现

  1、用户端的读控制器介绍

   FIFO IP核控制器读端口预留了两个FIFO接口,其中一个为命令FIFO,另一个为数据FIFO,我们需要将相应的信息发送到命令FIFO中,FIFO IP核检测到命令FIFO中有命令后,会根据命令的信息发送对应突发长度的数据到数据FIFO,我们检测到数据FIFO内存够了所需要的数据量,则可以将对应的数据读出即可。

  2、用户端的读控制器框架

 

   用户端的读模块需要一个rd_start作为输入,在rd_start信号有效后,该模块会输出命令FIFO所需的p1_cmd_en、p1_cmd_bl、p1_cmd_instr和p1_cmd_addr信号,之后FIFO IP核控制器会根据该信息发送对应突发长度的数据到数据FIFO,我们可以根据p1_rd_count的值判断数据FIFO的数据量,当数据FIFO内的数据量与所需要的数据量一致时,我们可以产生p1_rd_en信号到数据FIFO,将数据FIFO内的数据读出。

 3、端口变量说明

 

 4、用户端读控制的时序图

  由图4可以看出,在rd_start有效时,会产生读端口命令FIFO所需要的信号,这些信号会在p1_cmd_en有效的情况下被写入到读端的命令FIFO,之后DDR3 IP核控制器会将所需要的数据存入到读端的数据FIFO,我们根据p1_rd_count的值可以判断数据FIFO内的数据量是否满足本次突发,然后产生p1_rd_en读出已经满足本次突发的数据,当读完最后一个数据后,可以产生user_rd_end信号。

猜你喜欢

转载自www.cnblogs.com/tedymafpga/p/11813303.html
今日推荐