写模块总体框图
写模块分别用命令fifo和数据fifo里面读取数据;
a7_wr_start :指示写开始信号
a7_wr_bl :用户接口突发传输的数据个数n,则传输的总bit数位128*n。
app_rdy,app_wdf_rdy :来自IP核分别表示IP和准备好接受数据和写数据准备好写入。
data_req :数据fifo读出数据响应信号,同数据fifo读使能信号
a7_wr_data_end :一次突发传输结束信号,详细见时序图
写时序图
1.wr_bl:用于将数据缓存起来,因为a7_wr_start有效一个时钟周期
2.app_addr :用于缓存初始地址,并且没读入一个数据之后地址+(UI用户数据总线位宽)/(DDR3数据总线位宽)
2.wr_flag :指示可以写数据,遇到a7_wr_start有效止之后一直拉高即可,wr_flag拉低条件同cnt拉低条件
3.app_wdf_rdy :基本一直处于拉高状态,很少有为0的情况
4.app_rdy :出现低电平的可能性还是很高的
5.cnt :写入数据的个数计数,归零条件是:cnt==BL && app_rdy && app_wdf_rdy && wr_flag
6.app_addr :首地址由a7_wr_addr_initi决定,之后每次+8会伴随写入一个128bit的数据
7.此时序图反映的是数据和命令在同一个时钟写入的情况,其它两种情况原理相同
假设计数器cnt清零的条件是cntBL,wr_flag 拉低的条件是cnt == BL,如果是上面的时序图,这样最后一个数据无法写入,因为app_rdy在cntBL期间为低,也就是最后一个数据始终无法写入,所以cnt清零的条件是cnt==BL && app_rdy && app_wdf_rdy && wr_flag。
读时序的三种模式:from ug586 page of 167
写有三种模式,第三种模式数据迟于命令出来,最大不能超过两个周期。
//*************************************************************************//
//写模块的总体思路是先从命令fifo里面取指令,然后根据指令对数据fifo进行操作
//后续会有仲裁器判断读写模块哪个先执行
//*************************************************************************//
module a7_wr_ctrl #(
parameter BL_WIDTH = 7,
parameter ADDR_WIDRH = 28,
parameter DATA_WIDTH = 128,
)
(
input wire sclk , //模块工作时钟,连接到ui_clk即可
input wire sys_rst ,
input wire a7_wr_start ,
input wire [2:0] a7_wr_cmd , //直传给MIG的读写指令
input wire [BL_WIDTH-1:0] a7_wr_bl , //命令fifo读出的突发长度
input wire [ADDR_WIDRH-1:0] a7_wr_init_addr ,
input wire [DATA_WIDTH-1:0] a7_wdf_data , //数据fifo读出的数据
input wire [DATA_WIDTH/8-1:0] wr_mask ,
//
input wire app_rdy ,//指示向UI提供的数据是是否能被接收
input wire app_wdf_rdy ,
//OUTPUT
output wire app_en ,
output reg app_cmd ,
output wire app_wdf_wren ,
output reg [ADDR_WIDRH-1:0] app_addr ,
output wire [DATA_WIDTH-1:0] app_wdf_data ,
output wire app_wdf_end ,
output wire [DATA_WIDTH/8-1:0] app_wdf_mask ,
output wire data_req , //数据请求信号,也即数据fifo的读使能
output reg wr_end //所有数据传输完成指示
);
//内部变量定义
reg [BL_WIDTH-1:0] wr_bl ;//缓存输入进来的突发长度
reg wr_flag ;//指示写模块有效信号,在a7_wr_start之后有效
reg [BL_WIDTH-1:0] data_cnt;//输入数据个数计数器
reg [6:0] cmd_cnt ;//命令计数器
always @(posedge sclk or negedge sys_rst)
begin
if (sys_rst)
begin
wr_bl <= {BL_WIDTH{1'b0}} ;
app_addr <= {ADDR_WIDRH{1'b0}};
cnt <= {BL_WIDTH{1'b0}} ;
wr_end <= 1'b0 ;
data_cnt <= {BL_WIDTH{1'b0}} ;
end
else if (a7_wr_start) //外部输入的写开始信号,一个周期有效
begin
wr_flag <= 1'b1 ;
wr_bl <= a7_wr_bl ;
app_addr <= a7_wr_init_addr ;
end
else if (data_req && data_cnt == wr_bl - 1'b1)
begin
wr_flag <= 1'b0 ;
data_cnt <= {BL_WIDTH{1'b0}} ;
app_addr <= {ADDR_WIDRH{1'b0}} ;
end
else if (data_req)
begin
app_addr <= app_addr + 4'd8 ;
data_cnt <= data_cnt + 1'b1 ;
end
end
//app_cmd
assign app_amd = a7_wr_cmd ;
//app_wdf_mask
assign app_wdf_mask = wr_mask ;
//app_wdf_data
assign app_wdf_data = a7_wdf_data;
//app_wdf_end
assign app_wdf_end = app_en ;
//app_en
assign app_en = (app_rdy && app_wdf_rdy && wr_flag) ? 1b'1: 1'b0;
//data_req
assign data_req = (app_rdy && app_wdf_rdy && app_en ) ? 1b'1: 1'b0;
assign app_wdf_wren = (app_rdy && app_wdf_rdy && wr_flag) ? 1b'1: 1'b0;
//cmd_cnt
always@(posedge sclk)
if(rst_n==1'b0)
cmd_cnt <= 7'b0;
else if(cmd_cnt == wr_bl-1'b1 && app_rdy==1'b1)
cmd_cnt <= 7'b0;
else if(app_en==1'b1 && app_rdy == 1'b1)
cmd_cnt <= cmd_cnt+1'b1;
//wr_end
always@(posedge sclk)
if(rst_n==1'b0)
wr_end <= 1'b0;
else if(cmd_cnt == wr_bl-1'b1 && app_rdy==1'b1)
wr_end <= 1'b1;
else
wr_end <= 1'b0;
endmodule
控制逻辑的核心可以概括为:由app_en 和app_wdf_rdy控制,app_en控制是否可以写,app_wdf_rdy控制数据是否被接收。
补充知识DDR读写效率测试与分析 :http://xilinx.eetrend.com/d6-xilinx/article/2016-12/10776.html