本文转载自:巨大八爪鱼的博客
板子使用的是米联客的XC7A35TFGG484-2的开发板,上面带有256MB的型号为Micron MT41K128M16的DDR3内存。板子上的V4引脚上接了50MHz的晶振。
用MIG核来驱动这片DDR3内存。DDR3的运行时钟Clock Period为400MHz(由MIG核自己产生这个时钟,从ddr3_ck_p和ddr3_ck_n引脚输出出来,用来驱动DDR3):
因为PHY to Controller Clock Ratio为4:1,所以MIG核输出的ddr3_ui_clk时钟是400MHz进行四分频后得到的100MHz时钟。ddr3_ui_clk_sync_rst为低电平时,表示ddr3_ui_clk时钟已稳定,可以使用。
PLL Input Clock Period(sys_clk_i)为200MHz:
sys_clk_i和clk_ref_i都配置为No Buffer,然后在代码中都共用同一个200MHz的时钟:
.sys_clk_i(clock200), .clk_ref_i(clock200),
最后在综合的时候,选择Out of context per IP的方式:
这个200MHz的时钟由Clock Wizard核产生,使用MMCM模式,从外部晶振输入50MHz(clock),倍频后输出200MHz(clock200):
wire clock200; wire locked; clk_wiz_0 clk_wiz_0( .reset(0), .clk_in1(clock), .clk_out1(clock200), .locked(locked) );
当clock200时钟稳定时,locked信号将变为高电平。这个信号可用作其他系统的复位信号。
配置好之后,编译代码,在Implementation阶段,提示:
[Place 30-575] Sub-optimal placement for a clock-capable IO pin and MMCM pair. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
< set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets clk_wiz_0/inst/clk_in1_clk_wiz_0] >
clk_wiz_0/inst/clkin1_ibufg (IBUF.O) is locked to IOB_X1Y26
clk_wiz_0/inst/mmcm_adv_inst (MMCME2_ADV.CLKIN1) is provisionally placed by clockplacer on MMCME2_ADV_X1Y1
The above error could possibly be related to other connected instances. Following is a list of
all the related clock rules and their respective instances.
Clock Rule: rule_mmcm_bufg
Status: PASS
Rule Description: An MMCM driving a BUFG must be placed on the same half side (top/bottom) of the device
clk_wiz_0/inst/mmcm_adv_inst (MMCME2_ADV.CLKFBOUT) is provisionally placed by clockplacer on MMCME2_ADV_X1Y1
and clk_wiz_0/inst/clkf_buf (BUFG.I) is provisionally placed by clockplacer on BUFGCTRL_X0Y31
研究了一下午,最后在Xilinx官方的ug586_7Series_MIS手册里面发现:
这说明MIG核的时钟是可以用PLL或MMCM倍频输出的时钟的,不一定非要用引脚上接的晶振提供的时钟。配置后需要在自己的引脚约束文件*.xdc中加入下面这句话(从错误提示里面复制出来):
加了这句话之后,编译就可以通过了!这跟之前选的Global还是Out of context per IP没有一点关系。
下载到板子上运行,先写数据,然后是可以读出来的!
测试程序如下:
module main(
input clock,
inout [15:0] ddr3_dq,
inout [1:0] ddr3_dqs_n,
inout [1:0] ddr3_dqs_p,
output [13:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output ddr3_ck_p,
output ddr3_ck_n,
output ddr3_cke,
output ddr3_cs_n,
output [1:0] ddr3_dm,
output ddr3_odt,
output [3:0] leds,
output uart_tx
);
wire clock200;
wire locked;
clk_wiz_0 clk_wiz_0(
.reset(0),
.clk_in1(clock),
.clk_out1(clock200),
.locked(locked)
);
reg [27:0] ddr3_app_addr;
reg [2:0] ddr3_app_cmd;
reg ddr3_app_en;
reg [15:0] ddr3_app_wdf_data;
reg ddr3_app_wdf_end;
reg ddr3_app_wdf_wren;
wire [15:0] ddr3_app_rd_data;
wire ddr3_app_rd_data_end;
wire ddr3_app_rd_data_valid;
wire ddr3_app_rdy;
wire ddr3_app_wdf_rdy;
wire ddr3_app_sr_active;
wire ddr3_app_ref_ack;
wire ddr3_app_zq_ack;
wire ddr3_ui_clk;
wire ddr3_ui_clk_sync_rst;
wire ddr3_init_calib_complete;
wire [11:0] ddr3_device_temp;
mig_7series_0 mig_7series_0(
.ddr3_dq(ddr3_dq),
.ddr3_dqs_n(ddr3_dqs_n),
.ddr3_dqs_p(ddr3_dqs_p),
.ddr3_addr(ddr3_addr),
.ddr3_ba(ddr3_ba),
.ddr3_ras_n(ddr3_ras_n),
.ddr3_cas_n(ddr3_cas_n),
.ddr3_we_n(ddr3_we_n),
.ddr3_reset_n(ddr3_reset_n),
.ddr3_ck_p(ddr3_ck_p),
.ddr3_ck_n(ddr3_ck_n),
.ddr3_cke(ddr3_cke),
.ddr3_cs_n(ddr3_cs_n),
.ddr3_dm(ddr3_dm),
.ddr3_odt(ddr3_odt),
.sys_clk_i(clock200),
.clk_ref_i(clock200),
.app_addr(ddr3_app_addr),
.app_cmd(ddr3_app_cmd),
.app_en(ddr3_app_en),
.app_wdf_data(ddr3_app_wdf_data),
.app_wdf_end(ddr3_app_wdf_end),
.app_wdf_mask(2'b00),
.app_wdf_wren(ddr3_app_wdf_wren),
.app_rd_data(ddr3_app_rd_data),
.app_rd_data_end(ddr3_app_rd_data_end),
.app_rd_data_valid(ddr3_app_rd_data_valid),
.app_rdy(ddr3_app_rdy),
.app_wdf_rdy(ddr3_app_wdf_rdy),
.app_sr_req(1'b0),
.app_ref_req(1'b0),
.app_zq_req(1'b0),
.app_sr_active(ddr3_app_sr_active),
.app_ref_ack(ddr3_app_ref_ack),
.app_zq_ack(ddr3_app_zq_ack),
.ui_clk(ddr3_ui_clk),
.ui_clk_sync_rst(ddr3_ui_clk_sync_rst),
.init_calib_complete(ddr3_init_calib_complete),
.device_temp(ddr3_device_temp),
.sys_rst(locked)
);
wire uart_tx_request;
wire [7:0] uart_tx_data;
wire uart_tx_ready;
wire uart_sent;
UARTTransmitter uart_transmitter(clock, uart_tx, 24'd115200, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent);
reg uart_bytearray_tx_mode;
reg [15:0] uart_bytearray_tx_data;
reg uart_bytearray_tx_request = 0;
reg [7:0] uart_bytearray_tx_size;
wire uart_bytearray_tx_ready;
wire uart_bytearray_sent;
ByteArrayTransmitter #(2) uart_bytearray_transmitter(clock, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent,
uart_bytearray_tx_mode, uart_bytearray_tx_request, uart_bytearray_tx_data, uart_bytearray_tx_size, uart_bytearray_tx_ready, uart_bytearray_sent);
reg [3:0] state;
//assign leds = {locked, ddr3_ui_clk_sync_rst, ddr3_app_rdy, ddr3_app_wdf_rdy};
assign leds = state;
always @(posedge ddr3_ui_clk, posedge ddr3_ui_clk_sync_rst) begin
if (ddr3_ui_clk_sync_rst) begin
ddr3_app_en <= 0;
ddr3_app_wdf_wren <= 0;
state <= 0;
end
else begin
case (state)
0: begin
if (ddr3_app_en == 0) begin
if (ddr3_app_rdy && ddr3_app_wdf_rdy) begin
ddr3_app_cmd <= 0;
ddr3_app_addr <= 0;
ddr3_app_en <= 1;
ddr3_app_wdf_data <= 16'hfedc;
ddr3_app_wdf_wren <= 1;
ddr3_app_wdf_end <= 1;
end
end
else begin
ddr3_app_en <= 0;
ddr3_app_wdf_wren <= 0;
ddr3_app_wdf_end <= 0;
state <= 1;
end
end
1: begin
if (ddr3_app_en == 0) begin
if (ddr3_app_rdy) begin
ddr3_app_cmd <= 1;
ddr3_app_addr <= 0;
ddr3_app_en <= 1;
end
end
else if (ddr3_app_rd_data_valid) begin
uart_bytearray_tx_data <= ddr3_app_rd_data; // 读数据
ddr3_app_en <= 0;
state <= 2;
end
end
2: begin
if (uart_bytearray_tx_ready) begin
if (uart_bytearray_tx_request == 0) begin
uart_bytearray_tx_mode <= 0;
uart_bytearray_tx_size <= 2;
uart_bytearray_tx_request <= 1;
end
end
else
state <= 3;
end
3: begin
if (uart_bytearray_tx_ready) begin
// 字符串已发送
uart_bytearray_tx_request <= 0; // 关闭发送请求
state <= 4;
end
end
endcase
end
end
endmodule
【UARTTransmitter.v】
`include "config.vh" module UARTTransmitter( input clock, // 系统时钟 output reg tx = 1, // 串口发送引脚 input [23:0] baud_rate, // 波特率 input request, // 请求发送字符 (ready=1后应该及时撤销请求, 否则会再次发送同样的字符) input [7:0] data, // 发送的字符内容 (ready=0时必须保持不变) output ready, // 是否可以送入新字符 output sent // 是否发送完毕 ); integer counter = 0; // 波特率计数器 reg [3:0] bit_i = 10; // 当前正在发送第几位 (0为起始位, 1-8为数据位, 9为停止位, 10为空闲) wire bit_start = (counter == 0); // 位开始信号 wire bit_sample = (counter == `SYSCLK / baud_rate / 2 - 1); // 接收端采样点信号 wire bit_end = (counter == `SYSCLK / baud_rate - 1); // 位结束信号 /* ready引脚的真值表: bit_i request | ready ------------------------------------------------------------- 0-8 X | 0 (正在发送起始位和数据位) 9 X | 1 (正在发送停止位, 允许送入新字符) 10 0 | 1 (空闲, 允许送入新字符) 10 1 | 0 (已请求发送字符, 但还没开始发送) */ assign ready = (bit_i == 9 || sent); assign sent = (bit_i == 10 && !request); always @(posedge clock) begin if (bit_i <= 9) begin if (bit_start) begin counter <= 1; if (bit_i == 0) tx <= 0; // 起始位 else if (bit_i >= 1 && bit_i <= 8) tx <= data[bit_i - 1]; // 数据位 else tx <= 1; // 停止位 end else if (bit_end) begin counter <= 0; if (bit_i == 9 && request) bit_i <= 0; // 继续发送下一字符, 中间无停顿 else bit_i <= bit_i + 1'b1; // 继续发送下一位, 或停止发送 end else counter <= counter + 1; end else if (request) bit_i <= 0; // 开始发送 else counter <= 0; // 空闲 end endmodule
【ByteArrayTransmitter.v】
`define MAX_BIT (MAX_SIZE * 8 - 1) module ByteArrayTransmitter #( parameter MAX_SIZE = 16 )( input clock, output reg byte_request = 0, output reg [7:0] byte, input byte_ready, input byte_sent, input mode, // 0:二进制模式, 1:字符串模式 input request, input [`MAX_BIT:0] data, input [7:0] size, output ready, output sent ); localparam STATE_LOAD = 0; localparam STATE_REQUESTED = 1; localparam STATE_SENDING = 2; reg [`MAX_BIT:0] buffer; reg [7:0] count = 0; reg [1:0] state = STATE_LOAD; assign ready = (count == 0 && ((byte_ready && !byte_sent) || sent)); assign sent = (byte_sent && !request); always @(posedge clock) begin case (state) STATE_LOAD: begin if (byte_ready) begin if (count == size) count <= 0; // 发送结束 else if ((count == 0 && byte_sent && request && size > 0 && size <= MAX_SIZE) || count != 0) begin // 开始发送 if (count == 0) buffer = data << (8 * (MAX_SIZE - size)); // 载入请求发送的数据, 并靠左对齐 else buffer = buffer << 8; // 继续发送下一个字节 count <= count + 1'b1; byte = buffer[`MAX_BIT:`MAX_BIT - 7]; if (mode == 0 || byte != 0) begin byte_request <= 1; state <= STATE_REQUESTED; end end end end STATE_REQUESTED: begin // 等待发送开始 if (!byte_ready) state <= STATE_SENDING; end STATE_SENDING: begin // 等待发送结束 if (byte_ready) begin byte_request <= 0; state <= STATE_LOAD; end end endcase end endmodule
【config.vh】
`define SYSCLK 50000000 // 系统时钟频率
程序运行后,能够看到串口里面输出了FE DC,证明DDR3内存读写成功!
由于Test bench里面什么都没写,工程里面也没有引入DDR3的Simulation model,所以我们是无法仿真的,只能在真正的板子上调试程序。
【test.v】
module test(); reg clock = 0; always begin #10 clock = !clock; end main main(clock); endmodule
运行仿真后,app_rdy信号和init_calib_complete信号一直为低电平。而在真正的板子上并不是这样。