基于MIG IP核的DDR3控制器(二)
上一节中,记录到了ddr控制器的整体架构,在本节中,准备把ddr控制器的各个模块完善一下。
可以看到上一节中介绍了DDR控制器的整体架构,因为这几周事情多,又要课设什么的麻烦,今天抽点时间把这个记录完了,不然以后都忘了DDR该咋去控制了。
从本次实验的整体功能模块可以看出,最终我们只需要用户操作的信号为用户写入的256bit数据wr_ddr_data,写开始信号wr_start,数据请求信号data_req,读开始信号rd_start,读出的数据rd_ddr_data,读数据有效信号rd_data_vld,读结束和写结束信号rd_done、wr_done,ddr忙碌信号ddr_busy;
接下来我们将从顶层模块开始介绍各个模块实现的功能。
1) 顶层模块:
在顶层模块中,我们主要介绍用户相关的接口,在DDR3 存储器一侧的信号不做过多介绍
端口名称 |
I/O |
位宽 |
备注 |
wr_ddr_data |
I |
256 |
用户写入的256bit数据 |
data_req |
O |
1 |
向上游模块请求数据信号 |
wr_start |
I |
1 |
一次写ddr开始信号 |
wr_done |
O |
1 |
一次写ddr结束信号 |
rd_start |
I |
1 |
读开始信号 |
rd_data_vld |
O |
1 |
读出数据有效信号 |
rd_ddr_data |
O |
256 |
读出的有效数据 |
rd_done |
O |
1 |
一次突发读数据结束信号 |
ddr_busy |
O |
1 |
当前控制器处于忙碌状态 |
用户通过这些信号,能够较为简单地实现对DDR的访问。
2) 用户写控制模块
该模块的主要作用是,在接收到上游模块发送过来的写开始信号后,从上游模块将要写入DDR的数据请求而来,并将数据写入到DDR中。本模块的结构如下:
各个信号的作用如下表所示:
端口名称 |
I/O |
位宽 |
备注 |
ui_clk |
I |
1 |
系统时钟100M |
rst |
I |
1 |
系统复位,同步复位 |
wr_start |
I |
1 |
写开始信号 |
wr_ddr_data |
I |
256 |
用户写入的256bit数据 |
dta_req |
O |
1 |
向上游模块请求数据信号 |
wr_busy |
O |
1 |
当前模块正处于写忙碌状态 |
wr_done |
O |
1 |
一次写结束信号 |
wr_req |
O |
1 |
写请求信号,给到仲裁模块做总裁 |
wr_ack |
I |
1 |
写响应信号,仲裁模块给回的写响应 |
app_wdf_mask |
O |
32 |
32bit写入数据掩码 |
app_wdf_data |
O |
256 |
写入到DDR的数据 |
app_wdf_wren |
O |
1 |
当前写入数据有效信号 |
app_wdf_end |
O |
1 |
当前数据是ddr一次8突发的最后一个数据 |
app_wdf_rdy |
I |
1 |
当前MIG IP写数据通道处于空闲状态 |
app_rdy |
I |
1 |
当前MIG IP命令通道处于空闲状态 |
app_cmd |
O |
3 |
写数据命令3’b000 |
app_en |
O |
1 |
命令使能信号 |
app_addr |
O |
29 |
要访问的内存地址 |
关于本模块的代码设计,可以参考本模块的时序波形图,本模块状态跳转图如下,在IDLE状态下,若接收到写开始信号wr_start,则进入写请求状态,同时产生写请求wr_req给到仲裁模块,若当前可以进行写操作,则仲裁模块将会给出一个写响应信号wr_ack,接收到响应过后将跳转如写数据状态,从上游模块请求数据并给出写命令和地址,把数据写入到DDR中。
时序设计图如下:
对时序图做简单说明:进入写状态后,将使能app_wdf_wren信号,于此同时data_req信号在app_wdf_wren和app_wdf_rdy同时有效时才为有效,从而向上游模块请求数据,在写模块中,一次写操作需要向上游模块请求64个256bit数据。app_en在app_wdf_wren一个周期后拉高,然后再app_rdy和app_en同时有效的时候,需要给出写入ddr的地址,写入ddr的地址每次需要增加8,这是因为我们写入的数据是256bit,而ddr内存一个地址的容量是32bit,一次写入256bit地址正好增加8。当全部64个数据写入完成后,将产生一个写结束信号,指示本次写操作已经完成。在写状态时,拉高wr_busy指示当前模块正处于写忙碌的状态。
/*=============================================================================
#
# Author: weichaochen - 1530604142@qq.com
#
# QQ : 1530604142
#
# Last modified: 2019-12-29 19:28
#
# Filename: ddr_wr_ctrl.v
#
# Description:
#
=============================================================================*/
`timescale 1ns / 1ps module ddr_wr_ctrl(
input wire ui_clk ,//ddr3工作时钟
input wire rst ,//系统复位
input wire wr_start ,//写开始信号
output wire data_req ,//请求写数据信号
input wire [:] wr_ddr_data ,//将要写入的数据 output wire wr_req ,//写数据请求
input wire wr_ack ,//写响应
output wire wr_done ,//一次写结束
output wire wr_busy ,//当前处于忙碌状态 input wire app_rdy ,//命令通道空闲
output wire [:] app_cmd ,//输出的控制命令
output wire app_en ,//命令使能
output wire [:] app_addr ,//输出的地址 input wire app_wdf_rdy ,//写数据通道空闲信号
output wire [:] app_wdf_data,//写入的数据
output wire app_wdf_wren,//写入数据使能
output wire app_wdf_end ,//当前数据是DDR一次突发的最后一个数据
output wire [:] app_wdf_mask //写入数据掩码
); //=============================================
//parameter define
//=============================================
parameter IDLE = 'b001;
parameter WR_REQ = 'b010;
parameter WRITE = 'b100; parameter TOTAL_PIXEL = * - ;
parameter BURST_LEN = - ; //=============================================
//internal siganl
//=============================================
reg [:] state ;//状态寄存器 reg [:] cnt_data ;//计数当前写入的数据
wire add_cnt_data ;
wire end_cnt_data ; reg [:] cnt_cmd ;//计数当前已经给出的命令
wire add_cnt_cmd ;
wire end_cnt_cmd ; reg app_wdf_wren_r ;//写入数据有效
reg app_en_r ;//命令有效信号
reg [:] app_addr_r ;//写入的地址 reg wr_done_r ;//一次写完成
reg wr_req_r ;//写请求 assign app_cmd = 'b000; //写命令
assign app_wdf_mask = 'd0; //写数据掩码
assign app_wdf_wren = app_wdf_wren_r;
assign app_en = app_en_r ;
assign app_addr= app_addr_r;
assign app_wdf_data = wr_ddr_data;
assign wr_done = wr_done_r;
assign wr_busy = state==WRITE;
assign wr_req = wr_req_r;
assign app_wdf_end = app_wdf_wren_r; assign data_req = app_wdf_wren_r & app_wdf_rdy;//向上游模块请求数据 //--------------------state machine describe--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin
if(wr_start=='b1)
state <= WR_REQ;
else
state <= IDLE;
end WR_REQ:begin
if(wr_ack)
state <= WRITE;
else
state <= WR_REQ;
end WRITE:begin
if(end_cnt_cmd)
state <= IDLE;
else
state <= WRITE;
end default:begin
state <= IDLE;
end
endcase
end
end //--------------------app_wdf_wren_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
app_wdf_wren_r <= 'b0;
end
else if(end_cnt_data)begin
app_wdf_wren_r <= 'b0;
end
else if(state==WR_REQ && wr_ack=='b1)begin
app_wdf_wren_r <= 'b1;
end
end //--------------------cnt_data--------------------
always @(posedge ui_clk)begin
if(rst=='b1)begin
cnt_data <= ;
end
else if(add_cnt_data)begin
if(end_cnt_data)
cnt_data <= ;
else
cnt_data <= cnt_data + 'b1;
end
end assign add_cnt_data = data_req;
assign end_cnt_data = add_cnt_data && cnt_data== BURST_LEN; //--------------------app_en_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
app_en_r <= 'b0;
end
else if(end_cnt_cmd)begin
app_en_r <= 'b0;
end
else if(app_wdf_wren_r=='b1)begin
app_en_r <= 'b1;
end
end //--------------------cnt_cmd--------------------
always @(posedge ui_clk)begin
if(rst=='b1)begin
cnt_cmd <= ;
end
else if(add_cnt_cmd)begin
if(end_cnt_cmd)
cnt_cmd <= ;
else
cnt_cmd <= cnt_cmd + 'b1;
end
end assign add_cnt_cmd = app_rdy & app_en_r;
assign end_cnt_cmd = add_cnt_cmd && cnt_cmd== BURST_LEN; //--------------------app_addr_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
app_addr_r <= 'd0;
end
else if(app_addr_r == TOTAL_PIXEL && app_en_r=='b1 && app_rdy==1'b1)begin
app_addr_r <= 'd0;
end
else if(app_en_r=='b1 && app_rdy==1'b1)begin
app_addr_r <= app_addr_r + 'd8;
end
end //--------------------wr_done_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
wr_done_r <= 'b0;
end
else if(end_cnt_cmd=='b1)begin
wr_done_r <= 'b1;
end
else begin
wr_done_r <= 'b0;
end
end //--------------------wr_req_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
wr_req_r <= 'b0;
end
else if(wr_ack=='b1)begin
wr_req_r <= 'b0;
end
else if(state==IDLE && wr_start=='b1)begin
wr_req_r <= 'b1;
end
end endmodule
3) 用户读控制模块
本模块的作用是,当接收到读开始信号rd_start后,本模块产生读命令将数据从ddr中读出来,并将数据给到下游模块,本模块的结构图如下:
各个信号的作用如下表所示:
端口名称 |
I/O |
位宽 |
备注 |
ui_clk |
I |
1 |
系统时钟100M |
rst |
I |
1 |
系统复位,同步复位 |
rd_start |
I |
1 |
读开始信号 |
rd_ddra_data |
O |
256 |
从DDR中读出的数据 |
rd_data_vld |
O |
1 |
读出数据有效信号 |
rd_req |
O |
1 |
读请求信号,给到仲裁模块做判断 |
rd_ack |
I |
1 |
读响应信号,仲裁模块对读请求的响应 |
rd_done |
O |
1 |
一次读操作完成信号 |
rd_busy |
O |
1 |
当前模块正处于读忙碌状态 |
app_rdy |
I |
1 |
当前命令通道处于空闲状态 |
app_addr |
O |
29 |
读DDR的内存地址 |
app_en |
O |
1 |
读命令有效信号 |
app_cmd |
O |
3 |
读DDR的命令3’b001 |
app_rd_data |
I |
256 |
从DDR中读出的数据 |
app_rd_data_vld |
I |
1 |
从DDR中读出数据有效信号 |
app_rd_data_end |
I |
1 |
当前数据是DDR8突发的最后一个数据 |
本模块功能设计,可以参考用户读控制波形图,本模块的状态跳转图如下:若在空闲状态下接收到写开始信号rd_start则状态跳转到写请求状态,并给出写读请求信号rd_req,若接收到仲裁模块给出的读响应信号rd_ack,则跳转入读数据状态,将数据从ddr中读出。当读完64个数据后跳转回空闲状态。
本模块的时序设计如下:
在读状态下,将使能app_en信号,同时开始计数当前给出了多少个命令,在读模块中,一次读操作同样对应了64个数据,这样就与前面的写模块具有相同的长度,能够在读写速度上匹配,避免读写的冲突。当读完全部64个数据后,将给出读结束信号rd_done指示本次读操作已经完成,本模块处于读状态时,将使能rd_busy信号,指示本模块当前正忙碌。
/*=============================================================================
#
# Author: weichaochen - 1530604142@qq.com
#
# QQ : 1530604142
#
# Last modified: 2019-12-29 19:32
#
# Filename: ddr_rd_ctrl.v
#
# Description:
#
=============================================================================*/ `timescale 1ns / 1ps
module ddr_rd_ctrl(
input wire ui_clk ,//系统时钟
input wire rst ,//系统复位
input wire rd_start ,//读开始 output wire rd_req ,//读请求
input wire rd_ack ,//读响应
output wire rd_done ,//读完成
output wire rd_busy ,//读忙碌 output wire [:] app_cmd ,//读ddr命令
output wire app_en ,//读ddr命令使能
output wire [:] app_addr ,//读ddr地址
input wire app_rdy ,//ddr命令通道空闲 input wire app_rd_data_vld ,//ddr读出数据有效
input wire [:] app_rd_data ,//从ddr中读出的数据
output wire rd_ddr_data_vld ,//用户读出数据有效
output wire [:] rd_ddr_data //用户读出的数据
); //=============================================
//parameter define
//=============================================
parameter IDLE = 'b001;
parameter RD_REQ = 'b010;
parameter READ = 'b100; parameter TOTAL_PIXEL = * - ;
parameter BURST_LEN = - ; //=============================================
//internal siganl
//=============================================
reg [:] state ;//状态寄存器 reg [:] cnt_data ;//计数当读出的数据
wire add_cnt_data ;
wire end_cnt_data ; reg [:] cnt_cmd ;//计数当前已经给出的命令
wire add_cnt_cmd ;
wire end_cnt_cmd ; reg app_en_r ;//命令有效信号
reg [:] app_addr_r ;//写入的地址 reg rd_done_r ;//一次读完成
reg rd_req_r ;//读请求
reg rd_ddr_data_vld_r ;//读出数据有效
reg [:] rd_ddr_data_r ;//读出的数据 assign app_cmd = 'b001; //读命令
assign app_en = app_en_r ;
assign app_addr= app_addr_r;
assign rd_done = rd_done_r;
assign rd_busy = state==READ;
assign rd_req = rd_req_r;
assign rd_ddr_data = rd_ddr_data_r;
assign rd_ddr_data_vld = rd_ddr_data_vld_r; //--------------------state machine describe--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin
if(rd_start=='b1)
state <= RD_REQ;
else
state <= IDLE;
end RD_REQ:begin
if(rd_ack)
state <= READ;
else
state <= RD_REQ;
end READ:begin
if(end_cnt_cmd)
state <= IDLE;
else
state <= READ;
end default:begin
state <= IDLE;
end
endcase
end
end //--------------------cnt_data--------------------
always @(posedge ui_clk)begin
if(rst=='b1)begin
cnt_data <= ;
end
else if(add_cnt_data)begin
if(end_cnt_data)
cnt_data <= ;
else
cnt_data <= cnt_data + 'b1;
end
end assign add_cnt_data = app_rd_data_vld;
assign end_cnt_data = add_cnt_data && cnt_data== BURST_LEN; //--------------------app_en_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
app_en_r <= 'b0;
end
else if(end_cnt_cmd)begin
app_en_r <= 'b0;
end
else if(state==RD_REQ && rd_ack=='b1)begin
app_en_r <= 'b1;
end
end //--------------------cnt_cmd--------------------
always @(posedge ui_clk)begin
if(rst=='b1)begin
cnt_cmd <= ;
end
else if(add_cnt_cmd)begin
if(end_cnt_cmd)
cnt_cmd <= ;
else
cnt_cmd <= cnt_cmd + 'b1;
end
end assign add_cnt_cmd = app_rdy & app_en_r;
assign end_cnt_cmd = add_cnt_cmd && cnt_cmd== BURST_LEN; //--------------------app_addr_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
app_addr_r <= 'd0;
end
else if(app_addr_r == TOTAL_PIXEL && app_en_r=='b1 && app_rdy==1'b1)begin
app_addr_r <= 'd0;
end
else if(app_en_r=='b1 && app_rdy==1'b1)begin
app_addr_r <= app_addr_r + 'd8;
end
end //--------------------rd_done_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
rd_done_r <= 'b0;
end
else if(end_cnt_data=='b1)begin
rd_done_r <= 'b1;
end
else begin
rd_done_r <= 'b0;
end
end //--------------------rd_req_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
rd_req_r <= 'b0;
end
else if(rd_ack=='b1)begin
rd_req_r <= 'b0;
end
else if(state==IDLE && rd_start=='b1)begin
rd_req_r <= 'b1;
end
end //--------------------rd_ddr_data_r, rd_ddr_data_vld_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
rd_ddr_data_r <= 'd0;
rd_ddr_data_vld_r <= 'b0;
end
else begin
rd_ddr_data_r <= app_rd_data;
rd_ddr_data_vld_r <= app_rd_data_vld;
end
end
endmodule
4) 读写仲裁模块
本模块的目的是为了来避免读写冲突的,在ddr进行写操作的同时,也有可能有读操作需要处理,这时候就会发生读写冲突,为了避免读写操作同时发生创建一个仲裁模块,模块的结构和信号列表如下:
端口名称 |
I/O |
位宽 |
备注 |
wr_req |
I |
1 |
写数据请求,写控制模块给出的写请求 |
rd_req |
I |
1 |
读数据请求,读控制模块给出的读请求 |
ui_clk |
I |
1 |
系统时钟 |
rst |
I |
1 |
系统同步复位 |
wr_ack |
O |
1 |
写响应,用来响应本次写操作 |
wr_done |
I |
1 |
一次写操作完成 |
rd_ack |
O |
1 |
读响应,用来响应读操作 |
rd_done |
I |
1 |
一次读操作完成。 |
本模块的功能设计可以参考时序图,这里简单介绍以下本模块的工作方式,本模块的状态跳转入下图所示,在仲裁状态ARBIT下若接收到读请求,则进入读状态,若接收到写请求,则进入写状态,若读写请求同时出现则优先响应写请求进入写状态,当在读写状态中接收到读写结束信号wr_done或者rd_done时,状态将回到仲裁状态。
本模块的工作的时序设计如下:
本模块实现的功能比较简单,通过时序图能够很直观地就看出其功能,在这里不再赘述。至此我们完成了这些基础模块地创建,有了这些时序设计波形图,去设计代码就是十分简单地事情了。
/*=============================================================================
#
# Author: weichaochen - 1530604142@qq.com
#
# QQ : 1530604142
#
# Last modified: 2019-12-29 19:35
#
# Filename: ddr_arbit.v
#
# Description:
#
=============================================================================*/ `timescale 1ns / 1ps
module ddr_arbit(
input wire ui_clk ,//系统时钟
input wire rst ,//系统复位
input wire wr_req ,//写请求
output wire wr_ack ,//写响应
input wire wr_done ,//写完成 input wire rd_req ,//读请求
output wire rd_ack ,//读响应
input wire rd_done //读完成
); //==================================================
//parameter define
//==================================================
parameter IDLE = 'b0001;
parameter ARBIT = 'b0010;
parameter WRITE = 'b0100;
parameter READ = 'b1000; //==================================================
//internal siganls
//==================================================
reg wr_ack_r ;
reg rd_ack_r ;
reg [:] state ; assign wr_ack = wr_ack_r ;
assign rd_ack = rd_ack_r ; //--------------------state machine describe--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin
state <= ARBIT;
end ARBIT:begin
if(wr_req=='b1)//写的优先级高于读
state <= WRITE;
else if(wr_req=='b0 && rd_req==1'b1)
state <= READ;
end WRITE:begin
if(wr_done)
state <= ARBIT;
else
state <= WRITE;
end READ:begin
if(rd_done)
state <= ARBIT;
else
state <= READ;
end default:begin
state <= IDLE;
end
endcase
end
end //--------------------wr_ack_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
wr_ack_r <= 'b0;
end
else if(state==ARBIT && wr_req=='b1)begin
wr_ack_r <= 'b1;
end
else begin
wr_ack_r <= 'b0;
end
end //--------------------rd_ack_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
rd_ack_r <= 'b0;
end
else if(state==ARBIT && wr_req=='b0 && rd_req==1'b1)begin
rd_ack_r <= 'b1;
end
else begin
rd_ack_r <= 'b0;
end
end endmodule
在有了这些模块后,接下来我们要进行对这些模块的功能测试,我们将对ddr3进行循环读写测试来验证其功能。接下来,我们就编写一个测试模块。
该模块负责对DDR进行循环读写,每次读写64个256bit数据,通过比较写入和读出的数据是否相同来判断ddr控制器是否正常工作.
该模块的状态跳转如下图所示,在arbit状态下判断当前是该进行写还是读,应该注意本次实验的读写是交替进行的.
/*=============================================================================
#
# Author: weichaochen - 1530604142@qq.com
#
# QQ : 1530604142
#
# Last modified: 2019-12-29 19:41
#
# Filename: gen_test_data.v
#
# Description:
#
=============================================================================*/ `timescale 1ns / 1ps
module gen_test_data(
input wire ui_clk ,//系统时钟
input wire rst ,//系统复位
input wire ddr_busy ,//ddr控制器当前处于忙碌状态 output wire wr_start ,//写开始信号
input wire data_req ,//数据请求信号
output wire [:] wr_ddr_data ,//将要写入ddr的数据
input wire wr_done ,//一次写操作完成信号 output wire rd_start ,//读开始信号
input wire rd_data_vld ,//读出数据有效信号
input wire [:] rd_ddr_data ,//读出的有效数据
input wire rd_done ,//一次读完成信号 output wire error //读写错误信号
); //==================================================
//parameter define
//==================================================
parameter IDLE = 'b0001;
parameter ARBIT = 'b0010;
parameter WRITE = 'b0100;
parameter READ = 'b1000; parameter CNT_MAX = - ; //==================================================
//internal siganls
//==================================================
reg [:] state ;//状态寄存器
reg [:] cnt_data ;//数据计数
reg wr_start_r ;//写开始信号
reg rd_start_r ;//读开始信号
reg error_r ;//错误指示信号
reg wr_rd_flag ;//读写指示信号 assign wr_ddr_data = (wr_rd_flag=='b0)?{32{cnt_data}}:256'd0;
assign wr_start = wr_start_r;
assign rd_start = rd_start_r;
assign error = error_r;
//--------------------state machine describe--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
state <= IDLE;
end
else begin
case(state)
IDLE:begin
state <= ARBIT;
end ARBIT:begin
if(wr_start_r)//当前需要进行写操作
state <= WRITE;
else if(rd_start_r)//当前需要进行读操作
state <= READ;
end WRITE:begin
if(wr_done)
state <= ARBIT;
else
state <= WRITE;
end READ:begin
if(rd_done)
state <= ARBIT;
else
state <= READ;
end default:begin
state <= IDLE;
end
endcase
end
end //--------------------wr_rd_flag--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
wr_rd_flag <= 'b0;
end
else if(wr_done=='b1)begin
wr_rd_flag <= 'b1;
end
else if(rd_done=='b1)begin
wr_rd_flag <= 'b0;
end
end //--------------------wr_start_r,rd_start_r--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
wr_start_r <= 'b0;
rd_start_r <= 'b0;
end
else if(state==ARBIT && ddr_busy=='b0 && wr_rd_flag==1'b0)begin
wr_start_r <= 'b1;
end
else if(state==ARBIT && ddr_busy=='b0 && wr_rd_flag==1'b1)begin
rd_start_r <= 'b1;
end
else begin
rd_start_r <= 'b0;
wr_start_r <= 'b0;
end
end //--------------------cnt_data--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
cnt_data <= 'd0;
end
else if(data_req=='b1)begin
if(cnt_data==CNT_MAX)
cnt_data <= 'd0;
else
cnt_data <= cnt_data + 'b1;
end
else if(rd_data_vld=='b1)begin
if(cnt_data==CNT_MAX)
cnt_data <= 'd0;
else
cnt_data <= cnt_data + 'b1;
end
end //--------------------error--------------------
always @(posedge ui_clk)begin
if(rst == 'b1)begin
error_r <= 'b0;
end
else if(rd_data_vld=='b1 && (rd_ddr_data !={32{cnt_data}}))begin
error_r <= 'b1;
end
end endmodule
在接下来可以对对将整个工程进行仿真,通过仿真的结果来判断结构是否正确.
在产生循环读写的模块中,我们可以看到error信号一直保持为低,并且用户的确向DDR中写入了数据,并且也从DDR中读出了数据,说明我们的设计的控制器已经正常工作了。
基于MIG IP核的DDR3控制器(二)的更多相关文章
- 基于MIG IP核的DDR3控制器(一)
最近学习了DDR3控制器的使用,也用着DDR完成了一些简单工作,想着以后一段可能只用封装过后的IP核,可能会忘记DDR3控制器的一些内容,想着把这个DDR控制器的编写过程记录下来,便于我自己以后查看吧 ...
- MIG IP控制DDR3读写测试
本文设计思想采用明德扬至简设计法.在高速信号处理场合下,很短时间内就要缓存大量的数据,这时片内存储资源已经远远不够了.DDR SDRAM因其极高的性价比几乎是每一款中高档FPGA开发板的首选外部存储芯 ...
- FPGA上外挂DDR2&DDR3&MIG IP的使用记录
前言 当需要大容量数据存储及处理的时候,FPGA内部自带的存储资源是远远不够的,所以问题来了,怎么使用外带的DDR3? 首要问题在于DDR3是什么?有没有协议?当然只是需要用Xilinx MIG IP ...
- Vivado设计二:zynq的PS访问PL中的自带IP核(基于zybo)
1.建立工程 首先和Vivado设计一中一样,先建立工程(这部分就忽略了) 2.create block design 同样,Add IP 同样,也添加配置文件,这些都和设计一是一样的,没什么区别. ...
- DDR3(1):IP核调取
本系列整理一下基于 Xilinx A7 芯片的 DDR3 的使用,此处采用的 DDR3 IP核为软核,即采用 FPGA 逻辑单元.寄存器.查找表等搭建出来 IP核.从 IP 核的调取开始,接着读写测试 ...
- Xilinx 7系列例化MIG IP core DDR3读写
昨晚找了一下,发现DDR3读写在工程上多是通过例化MIG,调用生成IPcore的HDL Functional Model.我说嘛,自己哪能写出那么繁琐的,不过DDR读写数据可以用到状态机,后期再添砖加 ...
- 基于AXI4总线卷积FPGA加速IP核的尝试
本文先总结不同AXI IP核的实现的方法,性能的对比,性能差异的分析,可能改进的方面.使用的硬件平台是Zedboard. 不同的AXI总线卷积加速模块的概况 这次实现并逐渐优化了三个版本的卷积加速模块 ...
- Xilinx DDR3 IP核使用问题汇总(持续更新)和感悟
一度因为DDR3的IP核使用而发狂. 后来因为解决问题,得一感悟.后面此贴会完整讲述ddr3 ip的使用.(XILINX K7) 感悟:对于有供应商支持的产品,遇到问题找官方的流程.按照官方的指导进行 ...
- 基于Virtext6平台的GTX IP核基本设置说明
本工程基于以下条件使用: 板卡:DBF板v3.0 芯片型号:Virtex6 315T ISE版本:14.7 IP核版本: v6_gtxwizard : 1.12 一.IP核配置进行流程 第一页配置:线 ...
随机推荐
- 洛谷2387 BZOJ3669魔法森林题解
题目链接 BZ链接 这道题被很多人用spfa水了过去,表示很... 其实spfa很好卡,这组数据可以卡掉大多数spfa 链接:密码:rjvk 这里讲一下LCT的做法 我们按照a将边排序,然后依次添加 ...
- Lib1vent:10链接监听器接受TCP链接
evconnlistener机制提供了监听并接受TCP链接的方法.除非特别注明,本章的所有函数和类型都在event2/listener.h中声明. 一:创建或释放evconnlistener stru ...
- MaxCompute 图计算开发指南
快速入门step by step MaxCompute Studio 创建完成 MaxCompute Java Module后,即可以开始开发Graph了. 代码示例 在examples目录下有gra ...
- importError: DLL load failed when import matplotlib.pyplot as plt
importError: DLL load failed when import matplotlib.pyplot as plt 出现这种情况的原因, 大多是matplotlib的版本与python ...
- GIT 公钥配置
1.下载git 2.ssh-keygen -t rsa -C "xxx@xxx.com" 3.cd ~/.ssh 4.ls 5.cat id_rsa.pub 或者C:\User\x ...
- 模板—tarjan求割边
int dfn[MAXN],low[MAXN],cnt; void tarjan(int x,int edg) { low[x]=dfn[x]=++cnt; for(int i=f(x);i;i=n( ...
- 解锁当前XXX用户
pam_tally2 查看当前锁账户 pam_tally2 --user=XXX用户 --reset 解锁当前XXX用户
- 通过GDB重新获得进程的输出
有时通过SecureCRT或者Putty远程ssh到主机上执行某个进程,因长时间没有交互导致ssh断链,此时该进程由init进程收留.该进程的输出也就无法获得了. 这种情况下,可以利用gdb重新获得该 ...
- 31页PPT:基于Spark的移动大数据挖掘
31页PPT:基于Spark的移动大数据挖掘 数盟11.16 Data Science Meetup(DSM北京)分享:基于Spark的移动大数据挖掘分享嘉宾:张夏天(TalkingData首席数据科 ...
- 微信小程序下拉刷新 并重新加载数据
1.在json页面配置: { "enablePullDownRefresh": true } 2.调用刷新函数 onPullDownRefresh: function() { wx ...