基于MCB DDR3测试程序全地址空间测试
0赞作者:汤金元
日期:20150830
公司:南京米联电子科技有限公司
论坛:www.osrc.cn
博客:http://blog.chinaaet.com/whilebreak
1)、这个工程中,笔者设计了一个用户控制模块多MCB进行二次封装,实现流传输情况下,数据带宽的最大化,非常适合一些需要流传输的控制场合,应用起来非常方便。构架框如下图。
2)、下图的红色方框内,就是本次构架的关键代码
3)、mcb_user.v文件关键代码分析
读写命令控制模块
always @(posedge clk)begin if(!rst_n)begin mcb_cmd_instr <= MCB_CMD_RD; mcb_cmd_byte_addr <= u_rd_addr[29:0]; mcb_cmd_bl <= mcb_rd_bl; mcb_cmd_wr_p <=1'b0; mcb_cmd_rd_p <=1'b0; end else begin if(u_wr_cmd_en)// write begin mcb_cmd_instr <= MCB_CMD_WP; mcb_cmd_byte_addr <= u_wr_addr[29:0]; mcb_cmd_bl <= mcb_wr_bl; mcb_cmd_wr_p <=1'b1; mcb_cmd_rd_p <=1'b0; end else if(u_rd_cmd_en)//read begin mcb_cmd_instr <= MCB_CMD_RP; mcb_cmd_byte_addr <= u_rd_addr[29:0]; mcb_cmd_bl <= mcb_rd_bl; mcb_cmd_wr_p <=1'b0; mcb_cmd_rd_p <=1'b1; end else begin mcb_cmd_wr_p <=1'b0; mcb_cmd_rd_p <=1'b0; end end end |
mcb_cmd_instr :控制命令,一般是读命令或者写命名
mcb_cmd_byte_addr :控制命令地址,一般是写地址或者读地址
mcb_cmd_bl :控制命令长度,一般是写入数据的深度、或者需要读出数据的深度
mcb_cmd_wr_p:写使能同步
mcb_cmd_rd_p:读使能同步
以上模块实现了MCB读写命令控制,可以看出来当u_wr_cmd_en信号使能后就会实现一次写控制命令发送,当u_rd_cmd_en信号使能后就会实现一次读控制命令操作。并且从程序的接口看出来,写命令的优先级要高于读命令的优先级。
此文件中还有以下代码
mcb_cmd_en信号是读写命令使能信号
u_wr_cmd_done0 写命令完成
u_rd_cmd_done0 读命令完成
assign u_wr_cmd_done0 = mcb_cmd_en&(mcb_cmd_instr== MCB_CMD_WP);// user write cmd is done assign u_rd_cmd_done0 = mcb_cmd_en&(mcb_cmd_instr== MCB_CMD_RP);// user read cmd is done assign mcb_cmd_en = ((~mcb_cmd_wr_p1)&mcb_cmd_wr_p)||((~mcb_cmd_rd_p1)&mcb_cmd_rd_p);// mcb cmd enable |
以下这个模块 mcb_cmd_wr_p和mcb_cmd_rd_p,u_rd_cmd_done1 ,u_wr_cmd_done1实现信号的一个周期的延迟,u_rd_cmd_done通知用户读命令完成,u_wr_cmd_done
assign u_rd_cmd_done=u_rd_cmd_done1[1]; assign u_wr_cmd_done=u_wr_cmd_done1[1]; always@(posedge clk) begin if(!rst_n) begin mcb_cmd_wr_p1 <= 1'b0; mcb_cmd_rd_p1 <= 1'b0; u_wr_cmd_done1 <= 2'b0; u_rd_cmd_done1 <= 2'b0; end else begin mcb_cmd_wr_p1 <= mcb_cmd_wr_p; mcb_cmd_rd_p1 <= mcb_cmd_rd_p; u_rd_cmd_done1 <= {u_rd_cmd_done1[0:0],u_rd_cmd_done0}; u_wr_cmd_done1 <= {u_wr_cmd_done1[0:0],u_wr_cmd_done0}; end end |
读写FIFO使能模块
assign u_wr_rdy = mcb_wr_en;// write fifo is ready assign u_rd_rdy = mcb_rd_en;// read fifo is ready //write fifo enable and read fifo enable assign mcb_wr_en = (~mcb_wr_full)&&u_wr_en&&rst_n; assign mcb_rd_en = (~mcb_rd_empty)&&u_rd_en&&rst_n; |
以上代码mcb_wr_en代码写MCB FIFO使能,当mcb_wr_en为1时候数据写入MCB FIFO同时,u_wr_rdy为1通知写模块可以写数据。
以上代码mcb_rd_en代码读MCB FIFO使能,当mcb_rd_en为1时候数据从MCB FIFO读出,u_rd_rdy为1通知读模块可以读数据。
4)、u_mcb_write模块代码分析
module u_mcb_write( input clk, input rst_n, input u_wr_cmd_done,//user write cmd done input u_wr_rdy,// user write ready,data can be written in in to mcb output reg u_wr_cmd_en,//user write cmb cmd enable output reg u_wr_en,//user write enable output reg [127:0]u_wr_data,//user write data output reg [29:0]u_wr_addr,//user write address output reg [6 :0]u_wr_len // user data len ); (*KEEP = "TRUE" *) wire [1:0] u_wr_s_r; (*KEEP = "TRUE" *)wire [2:0] u_wr_en_dly1; parameter WR_IDLE = 2'd0; parameter WR_BEGIN = 2'd1; parameter WR_WAIT = 2'd2; assign u_wr_s_r = u_wr_s; reg [6 :0]u_wr_cnt; // user write counter reg [1 :0]u_wr_s; // user write state //when mcb write fifo more than 2 data enable write data mcb //写命令请求 always @(posedge clk) if(~rst_n) begin u_wr_cmd_en <= 1'b0; end else begin if(u_wr_cmd_done) u_wr_cmd_en <= 1'b0; // clear mcb user write enable signal else if(u_wr_cnt==40) u_wr_cmd_en <= 1'b1; // enable user mcb user cmd signal end //写测试数据产生 always @(posedge clk) if(~rst_n) begin u_wr_data<=128'hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA; end else begin if(u_wr_rdy) // mcb fifo is valid u_wr_data <= ~u_wr_data; // count up data write to mcb fifo else if(~u_wr_en) u_wr_data<=128'hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA; end //写数据计数器 always @(posedge clk) if(~rst_n) begin u_wr_cnt<=0; end else begin if(u_wr_rdy) // mcb fifo is valid u_wr_cnt <= u_wr_cnt + 1'b1; // count up data write to mcb fifo else if(u_wr_s==WR_IDLE) u_wr_cnt <= 7'd0; end reg [28:0] u_wr_addr_set; //写数据状态机 always @(posedge clk) if(~rst_n) begin u_wr_len <= 7'd64; // user write len u_wr_addr <= 29'd0; // user write address u_wr_en <= 1'b0; // user write enable u_wr_s <= WR_IDLE; end else begin case(u_wr_s) WR_IDLE: begin u_wr_en <= 1'b0; if(u_wr_cmd_en==1'b0) // start new write u_wr_s <= WR_BEGIN; end WR_BEGIN: begin u_wr_len <= 7'd64; u_wr_addr <= u_wr_addr_set; u_wr_en <= 1'b1; u_wr_s <= WR_WAIT; end WR_WAIT: if(u_wr_cnt==(u_wr_len-1)) begin u_wr_s <= WR_IDLE; u_wr_en <= 1'b0; end endcase end //888888888888888888888888888888888888888888888888888888888888888888888888 //地址空间起始地址产生 parameter ADDR_INC = 12'h400; parameter END_ADDR = 29'h10000000 - ADDR_INC; always @(posedge clk) if(~rst_n) begin u_wr_addr_set<=29'b0; end else begin if(u_wr_cmd_done&&(u_wr_addr_set<END_ADDR)) u_wr_addr_set<=u_wr_addr_set+ADDR_INC; else if(u_wr_addr_set==END_ADDR) u_wr_addr_set<=0; end endmodule |
从上面的程序还可以看出,如果同时有读写命令,优先处理写命令。
以上关键代码在于写命令请求的时机,当写MCB FIFO的数据非满就可以控制往MCB FIFO继续写数据。同时当FIFO有数据就可以发送控制命令往DDR把FIFO的数据搬运到DDR中。
另外地址空间的产生,MCB采用的地址空间是连续的,由于一次写入128bitX64的数据,因此一次写入的数据是1024 因此ADDR_INC = 12'h400;每次完成一次写命令地址空间增加一次。本开发板配备的内存大小为128MBX16 全地址空间为268435456B 那么最后一次的增量结束地址为
END_ADDR = 29'h10000000 - ADDR_INC
4)、u_mcb_read模块代码分析
module u_mcb_read ( input clk, input rst_n, input u_rd_cmd_done, //user read cmd done input u_rd_rdy, // user read ready,data can be written in in to mcb output reg u_rd_cmd_en, //user read cmb cmd enable output reg u_rd_en, //user read enable input [127:0]u_rd_data, //user read data output reg [29:0]u_rd_addr,//user read address output reg [6 :0]u_rd_len // user data len ); reg [1 :0]u_rd_s; // read state reg [6 :0]u_rd_cnt;// user read data counter reg read_error; (*KEEP = "TRUE" *) wire [1:0] u_rd_s_r;// debug signal assign u_rd_s_r = u_rd_s; (*KEEP = "TRUE" *) wire read_error_dg; assign read_error_dg = read_error; parameter RD_IDLE = 2'd0; parameter RD_BEGIN = 2'd1; parameter RD_WAIT = 2'd2; parameter RD_RST = 2'd3; reg u_rd_en_dly; always @(posedge clk) if(~rst_n) begin u_rd_en_dly<=1'b0; end else begin u_rd_en_dly <= u_rd_en; end //读命令请求 always @(posedge clk) if(~rst_n) begin u_rd_cmd_en <= 1'b0; end else begin if(u_rd_cmd_done) u_rd_cmd_en <= 1'b0; // clear user read mcb cmd signal else if(u_rd_en&&(~u_rd_en_dly)) u_rd_cmd_en <= 1'b1; // enable user read mbb cmd signal end reg s; //读数据比较 always @(posedge clk) if(~rst_n) begin read_error<=1'b0; s<=0; end else begin if((u_rd_s==RD_WAIT)&&u_rd_rdy)// valid data case(s) 0: if(u_rd_data==128'hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA) s <= 1'b1; else read_error<=1'b1; 1: if(u_rd_data==128'h55_55_55_55_55_55_55_55_55_55_55_55_55_55_55_55) s <= 1'b0; else read_error<=1'b1; endcase else if(~u_rd_en)begin read_error<=1'b0; s<=0; end end //读数据计数器 always @(posedge clk) if(~rst_n) begin u_rd_cnt<=0; end else begin if(u_rd_rdy)// valid data u_rd_cnt<=u_rd_cnt+1'b1; // count up else if(u_rd_s==RD_IDLE)u_rd_cnt<=7'd0; // clear counter end reg [28:0]u_rd_addr_set; //读状态机 always @(posedge clk) if(~rst_n) begin u_rd_len <= 7'd64; // user read length u_rd_addr <= 29'd0; // user read address u_rd_en <= 1'b0; // user read enable u_rd_s <= RD_IDLE; end else begin case(u_rd_s) RD_IDLE: begin u_rd_en <= 1'b0; if(u_rd_cmd_en==1'b0)// when mcb read cmd is done u_rd_s <= RD_BEGIN; end RD_BEGIN://read cmd begin u_rd_len <= 7'd64; u_rd_addr <= u_rd_addr_set; u_rd_en <= 1'b1; u_rd_s <= RD_WAIT; end RD_WAIT://read cmd done if(u_rd_cnt==(u_rd_len))begin u_rd_s <= RD_IDLE; u_rd_en <= 1'b0; end endcase end //888888888888888888888888888888888888888888888888888888888888888888888888 //地址空间起始地址产生 parameter ADDR_INC = 12'h400; parameter END_ADDR = 29'h10000000 - ADDR_INC; always @(posedge clk) if(~rst_n) begin u_rd_addr_set<=29'b0; end else begin if(u_rd_cmd_done&&(u_rd_addr_set<END_ADDR)) u_rd_addr_set<=u_rd_addr_set+ADDR_INC; else if(u_rd_addr_set==END_ADDR) u_rd_addr_set<=0; end endmodule |
以上关键代码在于读命令请求的时机,当写MCB FIFO的数据非空就可以控制从MCB FIFO继续读数据。同时当读FIFO非满数据就可以发送控制命令从DDR把数据搬运到读FIFO。
另外地址空间的产生,MCB采用的地址空间是连续的,由于一次读入128bitX64的数据,因此一次读入的数据是1024 因此ADDR_INC = 12'h400;每次完成一次读命令地址空间增加一次。本开发板配备的内存大小为128MBX16 全地址空间为268435456B 那么最后一次的增量结束地址为
END_ADDR = 29'h10000000 - ADDR_INC
MCB DDR3测试结果
本小结详细讲解了MCB内存控制 MIG CORE的产生过程,以及编写了全地址内存测试程序,对MIG CORE 重新封装构建,方便了后续开发的使用。