whilebreak

基于MCB DDR3测试程序全地址空间测试

0
阅读(3534)

作者:汤金元

日期:20150830

公司:南京米联电子科技有限公司

论坛:www.osrc.cn

网址:www.milinker.com

网店:http://osrc.taobao.com

博客:http://blog.chinaaet.com/whilebreak

1)、这个工程中,笔者设计了一个用户控制模块多MCB进行二次封装,实现流传输情况下,数据带宽的最大化,非常适合一些需要流传输的控制场合,应用起来非常方便。构架框如下图。

clip_image002

2)、下图的红色方框内,就是本次构架的关键代码

clip_image004

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测试结果

clip_image002

clip_image004[5]

clip_image006

本小结详细讲解了MCB内存控制 MIG CORE的产生过程,以及编写了全地址内存测试程序,对MIG CORE 重新封装构建,方便了后续开发的使用。