在Vivado 2020.1中用MIG核读写DDR3内存,编译代码时提示Sub-optimal placement错误的解决办法

板子使用的是米联客的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没有一点关系。

下载到板子上运行,先写数据,然后是可以读出来的!

测试程序如下:

【main.v】

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信号一直为低电平。而在真正的板子上并不是这样。

  • 7
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值