上一节中,记录到了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中读出了数据,说明我们的设计的控制器已经正常工作了。
原文出处:https://www.cnblogs.com/weicc/p/12051840.html
来源:oschina
链接:https://my.oschina.net/u/4291709/blog/3243469