基于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指示当前模块正处于写忙碌的状态。
1 /*============================================================================= 2 # 3 # Author: weichaochen - 1530604142@qq.com 4 # 5 # QQ : 1530604142 6 # 7 # Last modified: 2019-12-29 19:28 8 # 9 # Filename: ddr_wr_ctrl.v 10 # 11 # Description: 12 # 13 =============================================================================*/ 14 `timescale 1ns / 1ps 15 16 module ddr_wr_ctrl( 17 input wire ui_clk ,//ddr3工作时钟 18 input wire rst ,//系统复位 19 input wire wr_start ,//写开始信号 20 output wire data_req ,//请求写数据信号 21 input wire [255:0] wr_ddr_data ,//将要写入的数据 22 23 output wire wr_req ,//写数据请求 24 input wire wr_ack ,//写响应 25 output wire wr_done ,//一次写结束 26 output wire wr_busy ,//当前处于忙碌状态 27 28 input wire app_rdy ,//命令通道空闲 29 output wire [2:0] app_cmd ,//输出的控制命令 30 output wire app_en ,//命令使能 31 output wire [28:0] app_addr ,//输出的地址 32 33 input wire app_wdf_rdy ,//写数据通道空闲信号 34 output wire [255:0] app_wdf_data,//写入的数据 35 output wire app_wdf_wren,//写入数据使能 36 output wire app_wdf_end ,//当前数据是DDR一次突发的最后一个数据 37 output wire [31:0] app_wdf_mask //写入数据掩码 38 ); 39 40 //============================================= 41 //parameter define 42 //============================================= 43 parameter IDLE = 3'b001; 44 parameter WR_REQ = 3'b010; 45 parameter WRITE = 3'b100; 46 47 parameter TOTAL_PIXEL = 1024 * 768 - 8; 48 parameter BURST_LEN = 64 - 1; 49 50 //============================================= 51 //internal siganl 52 //============================================= 53 reg [2:0] state ;//状态寄存器 54 55 reg [9:0] cnt_data ;//计数当前写入的数据 56 wire add_cnt_data ; 57 wire end_cnt_data ; 58 59 reg [9:0] cnt_cmd ;//计数当前已经给出的命令 60 wire add_cnt_cmd ; 61 wire end_cnt_cmd ; 62 63 reg app_wdf_wren_r ;//写入数据有效 64 reg app_en_r ;//命令有效信号 65 reg [28:0] app_addr_r ;//写入的地址 66 67 reg wr_done_r ;//一次写完成 68 reg wr_req_r ;//写请求 69 70 71 72 73 assign app_cmd = 3'b000; //写命令 74 assign app_wdf_mask = 32'd0; //写数据掩码 75 assign app_wdf_wren = app_wdf_wren_r; 76 assign app_en = app_en_r ; 77 assign app_addr= app_addr_r; 78 assign app_wdf_data = wr_ddr_data; 79 assign wr_done = wr_done_r; 80 assign wr_busy = state==WRITE; 81 assign wr_req = wr_req_r; 82 assign app_wdf_end = app_wdf_wren_r; 83 84 assign data_req = app_wdf_wren_r & app_wdf_rdy;//向上游模块请求数据 85 86 87 //--------------------state machine describe-------------------- 88 always @(posedge ui_clk)begin 89 if(rst == 1'b1)begin 90 state <= IDLE; 91 end 92 else begin 93 case(state) 94 IDLE:begin 95 if(wr_start==1'b1) 96 state <= WR_REQ; 97 else 98 state <= IDLE; 99 end 100 101 WR_REQ:begin 102 if(wr_ack) 103 state <= WRITE; 104 else 105 state <= WR_REQ; 106 end 107 108 WRITE:begin 109 if(end_cnt_cmd) 110 state <= IDLE; 111 else 112 state <= WRITE; 113 end 114 115 default:begin 116 state <= IDLE; 117 end 118 endcase 119 end 120 end 121 122 123 //--------------------app_wdf_wren_r-------------------- 124 always @(posedge ui_clk)begin 125 if(rst == 1'b1)begin 126 app_wdf_wren_r <= 1'b0; 127 end 128 else if(end_cnt_data)begin 129 app_wdf_wren_r <= 1'b0; 130 end 131 else if(state==WR_REQ && wr_ack==1'b1)begin 132 app_wdf_wren_r <= 1'b1; 133 end 134 end 135 136 //--------------------cnt_data-------------------- 137 always @(posedge ui_clk)begin 138 if(rst==1'b1)begin 139 cnt_data <= 0; 140 end 141 else if(add_cnt_data)begin 142 if(end_cnt_data) 143 cnt_data <= 0; 144 else 145 cnt_data <= cnt_data + 1'b1; 146 end 147 end 148 149 assign add_cnt_data = data_req; 150 assign end_cnt_data = add_cnt_data && cnt_data== BURST_LEN; 151 152 //--------------------app_en_r-------------------- 153 always @(posedge ui_clk)begin 154 if(rst == 1'b1)begin 155 app_en_r <= 1'b0; 156 end 157 else if(end_cnt_cmd)begin 158 app_en_r <= 1'b0; 159 end 160 else if(app_wdf_wren_r==1'b1)begin 161 app_en_r <= 1'b1; 162 end 163 end 164 165 //--------------------cnt_cmd-------------------- 166 always @(posedge ui_clk)begin 167 if(rst==1'b1)begin 168 cnt_cmd <= 0; 169 end 170 else if(add_cnt_cmd)begin 171 if(end_cnt_cmd) 172 cnt_cmd <= 0; 173 else 174 cnt_cmd <= cnt_cmd + 1'b1; 175 end 176 end 177 178 assign add_cnt_cmd = app_rdy & app_en_r; 179 assign end_cnt_cmd = add_cnt_cmd && cnt_cmd== BURST_LEN; 180 181 //--------------------app_addr_r-------------------- 182 always @(posedge ui_clk)begin 183 if(rst == 1'b1)begin 184 app_addr_r <= 'd0; 185 end 186 else if(app_addr_r == TOTAL_PIXEL && app_en_r==1'b1 && app_rdy==1'b1)begin 187 app_addr_r <= 'd0; 188 end 189 else if(app_en_r==1'b1 && app_rdy==1'b1)begin 190 app_addr_r <= app_addr_r + 'd8; 191 end 192 end 193 194 //--------------------wr_done_r-------------------- 195 always @(posedge ui_clk)begin 196 if(rst == 1'b1)begin 197 wr_done_r <= 1'b0; 198 end 199 else if(end_cnt_cmd==1'b1)begin 200 wr_done_r <= 1'b1; 201 end 202 else begin 203 wr_done_r <= 1'b0; 204 end 205 end 206 207 //--------------------wr_req_r-------------------- 208 always @(posedge ui_clk)begin 209 if(rst == 1'b1)begin 210 wr_req_r <= 1'b0; 211 end 212 else if(wr_ack==1'b1)begin 213 wr_req_r <= 1'b0; 214 end 215 else if(state==IDLE && wr_start==1'b1)begin 216 wr_req_r <= 1'b1; 217 end 218 end 219 220 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信号,指示本模块当前正忙碌。
1 /*============================================================================= 2 # 3 # Author: weichaochen - 1530604142@qq.com 4 # 5 # QQ : 1530604142 6 # 7 # Last modified: 2019-12-29 19:32 8 # 9 # Filename: ddr_rd_ctrl.v 10 # 11 # Description: 12 # 13 =============================================================================*/ 14 15 `timescale 1ns / 1ps 16 module ddr_rd_ctrl( 17 input wire ui_clk ,//系统时钟 18 input wire rst ,//系统复位 19 input wire rd_start ,//读开始 20 21 output wire rd_req ,//读请求 22 input wire rd_ack ,//读响应 23 output wire rd_done ,//读完成 24 output wire rd_busy ,//读忙碌 25 26 output wire [2:0] app_cmd ,//读ddr命令 27 output wire app_en ,//读ddr命令使能 28 output wire [28:0] app_addr ,//读ddr地址 29 input wire app_rdy ,//ddr命令通道空闲 30 31 input wire app_rd_data_vld ,//ddr读出数据有效 32 input wire [255:0] app_rd_data ,//从ddr中读出的数据 33 output wire rd_ddr_data_vld ,//用户读出数据有效 34 output wire [255:0] rd_ddr_data //用户读出的数据 35 ); 36 37 //============================================= 38 //parameter define 39 //============================================= 40 parameter IDLE = 3'b001; 41 parameter RD_REQ = 3'b010; 42 parameter READ = 3'b100; 43 44 parameter TOTAL_PIXEL = 1024 * 768 - 8; 45 parameter BURST_LEN = 64 - 1; 46 47 //============================================= 48 //internal siganl 49 //============================================= 50 reg [2:0] state ;//状态寄存器 51 52 reg [9:0] cnt_data ;//计数当读出的数据 53 wire add_cnt_data ; 54 wire end_cnt_data ; 55 56 reg [9:0] cnt_cmd ;//计数当前已经给出的命令 57 wire add_cnt_cmd ; 58 wire end_cnt_cmd ; 59 60 reg app_en_r ;//命令有效信号 61 reg [28:0] app_addr_r ;//写入的地址 62 63 reg rd_done_r ;//一次读完成 64 reg rd_req_r ;//读请求 65 reg rd_ddr_data_vld_r ;//读出数据有效 66 reg [255:0] rd_ddr_data_r ;//读出的数据 67 68 assign app_cmd = 3'b001; //读命令 69 assign app_en = app_en_r ; 70 assign app_addr= app_addr_r; 71 assign rd_done = rd_done_r; 72 assign rd_busy = state==READ; 73 assign rd_req = rd_req_r; 74 assign rd_ddr_data = rd_ddr_data_r; 75 assign rd_ddr_data_vld = rd_ddr_data_vld_r; 76 77 78 //--------------------state machine describe-------------------- 79 always @(posedge ui_clk)begin 80 if(rst == 1'b1)begin 81 state <= IDLE; 82 end 83 else begin 84 case(state) 85 IDLE:begin 86 if(rd_start==1'b1) 87 state <= RD_REQ; 88 else 89 state <= IDLE; 90 end 91 92 RD_REQ:begin 93 if(rd_ack) 94 state <= READ; 95 else 96 state <= RD_REQ; 97 end 98 99 READ:begin 100 if(end_cnt_cmd) 101 state <= IDLE; 102 else 103 state <= READ; 104 end 105 106 default:begin 107 state <= IDLE; 108 end 109 endcase 110 end 111 end 112 113 114 //--------------------cnt_data-------------------- 115 always @(posedge ui_clk)begin 116 if(rst==1'b1)begin 117 cnt_data <= 0; 118 end 119 else if(add_cnt_data)begin 120 if(end_cnt_data) 121 cnt_data <= 0; 122 else 123 cnt_data <= cnt_data + 1'b1; 124 end 125 end 126 127 assign add_cnt_data = app_rd_data_vld; 128 assign end_cnt_data = add_cnt_data && cnt_data== BURST_LEN; 129 130 //--------------------app_en_r-------------------- 131 always @(posedge ui_clk)begin 132 if(rst == 1'b1)begin 133 app_en_r <= 1'b0; 134 end 135 else if(end_cnt_cmd)begin 136 app_en_r <= 1'b0; 137 end 138 else if(state==RD_REQ && rd_ack==1'b1)begin 139 app_en_r <= 1'b1; 140 end 141 end 142 143 //--------------------cnt_cmd-------------------- 144 always @(posedge ui_clk)begin 145 if(rst==1'b1)begin 146 cnt_cmd <= 0; 147 end 148 else if(add_cnt_cmd)begin 149 if(end_cnt_cmd) 150 cnt_cmd <= 0; 151 else 152 cnt_cmd <= cnt_cmd + 1'b1; 153 end 154 end 155 156 assign add_cnt_cmd = app_rdy & app_en_r; 157 assign end_cnt_cmd = add_cnt_cmd && cnt_cmd== BURST_LEN; 158 159 //--------------------app_addr_r-------------------- 160 always @(posedge ui_clk)begin 161 if(rst == 1'b1)begin 162 app_addr_r <= 'd0; 163 end 164 else if(app_addr_r == TOTAL_PIXEL && app_en_r==1'b1 && app_rdy==1'b1)begin 165 app_addr_r <= 'd0; 166 end 167 else if(app_en_r==1'b1 && app_rdy==1'b1)begin 168 app_addr_r <= app_addr_r + 'd8; 169 end 170 end 171 172 //--------------------rd_done_r-------------------- 173 always @(posedge ui_clk)begin 174 if(rst == 1'b1)begin 175 rd_done_r <= 1'b0; 176 end 177 else if(end_cnt_data==1'b1)begin 178 rd_done_r <= 1'b1; 179 end 180 else begin 181 rd_done_r <= 1'b0; 182 end 183 end 184 185 //--------------------rd_req_r-------------------- 186 always @(posedge ui_clk)begin 187 if(rst == 1'b1)begin 188 rd_req_r <= 1'b0; 189 end 190 else if(rd_ack==1'b1)begin 191 rd_req_r <= 1'b0; 192 end 193 else if(state==IDLE && rd_start==1'b1)begin 194 rd_req_r <= 1'b1; 195 end 196 end 197 198 //--------------------rd_ddr_data_r, rd_ddr_data_vld_r-------------------- 199 always @(posedge ui_clk)begin 200 if(rst == 1'b1)begin 201 rd_ddr_data_r <= 'd0; 202 rd_ddr_data_vld_r <= 1'b0; 203 end 204 else begin 205 rd_ddr_data_r <= app_rd_data; 206 rd_ddr_data_vld_r <= app_rd_data_vld; 207 end 208 end 209 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 = 4'b0001; parameter ARBIT = 4'b0010; parameter WRITE = 4'b0100; parameter READ = 4'b1000; //================================================== //internal siganls //================================================== reg wr_ack_r ; reg rd_ack_r ; reg [3:0] state ; assign wr_ack = wr_ack_r ; assign rd_ack = rd_ack_r ; //--------------------state machine describe-------------------- always @(posedge ui_clk)begin if(rst == 1'b1)begin state <= IDLE; end else begin case(state) IDLE:begin state <= ARBIT; end ARBIT:begin if(wr_req==1'b1)//写的优先级高于读 state <= WRITE; else if(wr_req==1'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 == 1'b1)begin wr_ack_r <= 1'b0; end else if(state==ARBIT && wr_req==1'b1)begin wr_ack_r <= 1'b1; end else begin wr_ack_r <= 1'b0; end end //--------------------rd_ack_r-------------------- always @(posedge ui_clk)begin if(rst == 1'b1)begin rd_ack_r <= 1'b0; end else if(state==ARBIT && wr_req==1'b0 && rd_req==1'b1)begin rd_ack_r <= 1'b1; end else begin rd_ack_r <= 1'b0; end end endmodule
在有了这些模块后,接下来我们要进行对这些模块的功能测试,我们将对ddr3进行循环读写测试来验证其功能。接下来,我们就编写一个测试模块。
该模块负责对DDR进行循环读写,每次读写64个256bit数据,通过比较写入和读出的数据是否相同来判断ddr控制器是否正常工作.
该模块的状态跳转如下图所示,在arbit状态下判断当前是该进行写还是读,应该注意本次实验的读写是交替进行的.
1 /*============================================================================= 2 # 3 # Author: weichaochen - 1530604142@qq.com 4 # 5 # QQ : 1530604142 6 # 7 # Last modified: 2019-12-29 19:41 8 # 9 # Filename: gen_test_data.v 10 # 11 # Description: 12 # 13 =============================================================================*/ 14 15 `timescale 1ns / 1ps 16 module gen_test_data( 17 input wire ui_clk ,//系统时钟 18 input wire rst ,//系统复位 19 input wire ddr_busy ,//ddr控制器当前处于忙碌状态 20 21 output wire wr_start ,//写开始信号 22 input wire data_req ,//数据请求信号 23 output wire [255:0] wr_ddr_data ,//将要写入ddr的数据 24 input wire wr_done ,//一次写操作完成信号 25 26 output wire rd_start ,//读开始信号 27 input wire rd_data_vld ,//读出数据有效信号 28 input wire [255:0] rd_ddr_data ,//读出的有效数据 29 input wire rd_done ,//一次读完成信号 30 31 output wire error //读写错误信号 32 ); 33 34 //================================================== 35 //parameter define 36 //================================================== 37 parameter IDLE = 4'b0001; 38 parameter ARBIT = 4'b0010; 39 parameter WRITE = 4'b0100; 40 parameter READ = 4'b1000; 41 42 parameter CNT_MAX = 64 - 1; 43 44 //================================================== 45 //internal siganls 46 //================================================== 47 reg [3:0] state ;//状态寄存器 48 reg [7:0] cnt_data ;//数据计数 49 reg wr_start_r ;//写开始信号 50 reg rd_start_r ;//读开始信号 51 reg error_r ;//错误指示信号 52 reg wr_rd_flag ;//读写指示信号 53 54 assign wr_ddr_data = (wr_rd_flag==1'b0)?{32{cnt_data}}:256'd0; 55 assign wr_start = wr_start_r; 56 assign rd_start = rd_start_r; 57 assign error = error_r; 58 //--------------------state machine describe-------------------- 59 always @(posedge ui_clk)begin 60 if(rst == 1'b1)begin 61 state <= IDLE; 62 end 63 else begin 64 case(state) 65 IDLE:begin 66 state <= ARBIT; 67 end 68 69 ARBIT:begin 70 if(wr_start_r)//当前需要进行写操作 71 state <= WRITE; 72 else if(rd_start_r)//当前需要进行读操作 73 state <= READ; 74 end 75 76 WRITE:begin 77 if(wr_done) 78 state <= ARBIT; 79 else 80 state <= WRITE; 81 end 82 83 READ:begin 84 if(rd_done) 85 state <= ARBIT; 86 else 87 state <= READ; 88 end 89 90 default:begin 91 state <= IDLE; 92 end 93 endcase 94 end 95 end 96 97 //--------------------wr_rd_flag-------------------- 98 always @(posedge ui_clk)begin 99 if(rst == 1'b1)begin 100 wr_rd_flag <= 1'b0; 101 end 102 else if(wr_done==1'b1)begin 103 wr_rd_flag <= 1'b1; 104 end 105 else if(rd_done==1'b1)begin 106 wr_rd_flag <= 1'b0; 107 end 108 end 109 110 //--------------------wr_start_r,rd_start_r-------------------- 111 always @(posedge ui_clk)begin 112 if(rst == 1'b1)begin 113 wr_start_r <= 1'b0; 114 rd_start_r <= 1'b0; 115 end 116 else if(state==ARBIT && ddr_busy==1'b0 && wr_rd_flag==1'b0)begin 117 wr_start_r <= 1'b1; 118 end 119 else if(state==ARBIT && ddr_busy==1'b0 && wr_rd_flag==1'b1)begin 120 rd_start_r <= 1'b1; 121 end 122 else begin 123 rd_start_r <= 1'b0; 124 wr_start_r <= 1'b0; 125 end 126 end 127 128 //--------------------cnt_data-------------------- 129 always @(posedge ui_clk)begin 130 if(rst == 1'b1)begin 131 cnt_data <= 'd0; 132 end 133 else if(data_req==1'b1)begin 134 if(cnt_data==CNT_MAX) 135 cnt_data <= 'd0; 136 else 137 cnt_data <= cnt_data + 1'b1; 138 end 139 else if(rd_data_vld==1'b1)begin 140 if(cnt_data==CNT_MAX) 141 cnt_data <= 'd0; 142 else 143 cnt_data <= cnt_data + 1'b1; 144 end 145 end 146 147 //--------------------error-------------------- 148 always @(posedge ui_clk)begin 149 if(rst == 1'b1)begin 150 error_r <= 1'b0; 151 end 152 else if(rd_data_vld==1'b1 && (rd_ddr_data !={32{cnt_data}}))begin 153 error_r <= 1'b1; 154 end 155 end 156 157 158 endmodule
在接下来可以对对将整个工程进行仿真,通过仿真的结果来判断结构是否正确.
在产生循环读写的模块中,我们可以看到error信号一直保持为低,并且用户的确向DDR中写入了数据,并且也从DDR中读出了数据,说明我们的设计的控制器已经正常工作了。