ZYNQ 7020 FPGA DDR3

1 篇文章 0 订阅

ZYNQ 7020 FPGA DDR3

ZYNQ是FPGA+双核ARM的架构

最近我需要用到ZYNQ来做东西,但是网上看见到的开发板在FPGA部分没有没有挂DDR3,只能结合自己以前的经验和现有的资料,摸索一下。本文分为两部分:
1. zynq的FPGA部分怎么加上两片ddr3,引脚分配,时钟等。
2. 如何测试,验证PCB效果。

  1. 注意下面的FPGA Part和速度等级,确保选择的是自己使用的芯片,之后选择下一步。
    图1
    |:------------------------------------😐 图1

  2. 下一步。
    在这里插入图片描述
    |:------------------------------------😐 图2

  3. 这里有三个注意:
    3.1我使用的是XC7Z020CLG484-2,最大支持DDR3时钟是400Mhz。
    3.2我用的DDR3型号mt41k256m16tw-107it:p,找不到,只能选择相近的型号,但是都是兼容的。

    3.3我是两片ddr3,共计32bit,我见有些人用一片,那这里就是16。
    在这里插入图片描述
    |:------------------------------------😐 图3

4.重点来了,这里的选择卡了我很久,因为ZYNQ7020的时钟是50Mhz,我不知道这里是填200Mhz还是选择50Mhz,最后看到米联客的讲解,我用一个PLL,50Mhz转200Mhz做MIG的时钟输入不久行了嘛,所以此处果断填200Mhz.(另外如果此处不填200Mhz,图6中的system clock选项没有use system clock选项,见图7)
在这里插入图片描述
|:------------------------------------😐 图5

  1. 这里的system clock选择no buffer,reference clock选择use system clock。
    在这里插入图片描述
    |:------------------------------------😐 图6
  2. 下面这个图就是因为前面图5没有选择200Mhz时钟时,才会没有use system clock的选项。
    在这里插入图片描述
    |:------------------------------------😐 图7

7.选择Fixed Pin Out,让MIG向导帮助我们实现DDR3引脚的一些分配。
在这里插入图片描述
|:------------------------------------😐 图8

8.最左侧是两片DDR3需要的所有信号脚的名称。其中32个数据脚是8bit为一组,那就拿ddr3_dq[7:0]举例,可以见下图,我分了bank35给DDR3使用。ByteNumber选择了T2,Pin Number是因为选择了T2,MIG给了我一个范围(见图10),我在这些范围里面选择了上面的脚,我在PCB lyout时,发现选的不好,又来回调整过几回。

8.1 一开始考虑过一个问题,ZYNQ的FPGA一个bank够不够给两片ddr3用,最后发现不管是那个bank都是不够的,差一个脚。此时真心想…
8.2 T0~T3,每个都对应着一些脚,供大家选择。
在这里插入图片描述
|:------------------------------------😐 图9
9. 可以看见Pin Number选框下面对应着很多的脚,每选择一个脚,对应就会出现一道杠,表示这个脚被用了。
在这里插入图片描述
|:------------------------------------😐 图10

10.下面的一些脚分配,方法雷同,请各位大胆摸索,大不了重建个MIG(啊,我建了多少个,自己都忘了)
在这里插入图片描述
|:------------------------------------😐 图11
11.MIG支持你之前做好的引脚分配,选择read XDC/UCF按钮,就可以将之前做好的引脚分配文件导入。
在这里插入图片描述
|:------------------------------------😐 图12
12. 当你的引脚分配完毕之后,要用validate按钮验证一下,如果验证成功之后,next按钮才可以点击,否则是点不动的,不能进行下一步操作。当然,如果你验证成功之后,记得点击Save Pin Out,将你自己分配好的引脚做一下保存,否则当你重新打开MIG之后,你会发现,卧槽,刚刚分配好的咋没有了。这种感受你最好别体验,真难受。
在这里插入图片描述
|:------------------------------------😐 图13

  1. 下一步。
    在这里插入图片描述
    |:------------------------------------😐 图14

  2. 做了一个总结,有兴趣自己看吧。下一步。
    在这里插入图片描述
    |:------------------------------------😐 图15

15.当然是接收啦。下一步。
在这里插入图片描述
|:------------------------------------😐 图16

  1. 点击generate。恭喜你,或许你的ddr3引脚分配完成啦。导入到自己的原理图里面吧。
    在这里插入图片描述
    |:------------------------------------😐 图17

17.在你的工程里面会生成一个MIG 的IP核。
在这里插入图片描述


下面就是验证用的代码部分,代码的来源是米联客的DDR3教程,感谢米联客的团队。
我根据自己的情况做了一些修改:
1.例化PLL,实现50Mhz转200Mhz。
2.例程上面说的是一片DDR3,但是我用的是两片,代码里面有些参数是需要更改的。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/07/12 16:14:13
// Design Name: 
// Module Name: mig_ddr_test
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module mig_ddr_test
(
	inout 		[31:0] ddr3_dq,			// 我的ddr3是32bit		
	inout 		[3:0] ddr3_dqs_n,		// 对应四个数据选通
	inout 		[3:0] ddr3_dqs_p,		// 上面信号的差分
	output 	[14:0] ddr3_addr,			// 我的地址一共是14bit
	output 	[2:0] ddr3_ba,
	output		 ddr3_ras_n,
	output ddr3_cas_n,
	output ddr3_we_n,
	output ddr3_reset_n,
	output [0:0] ddr3_ck_p,
	output [0:0] ddr3_ck_n,
	output [0:0] ddr3_cke,
	output [0:0] ddr3_cs_n,
	output [3:0] ddr3_dm,		// 这里4bit的数据屏蔽位
	output [0:0] ddr3_odt,
	input sysclk_i,				// 这里就是我上面说的200Mh时钟输入
								// 我用了一个PLL,50Mhz转200Mhz
	output tg_compare_error,
	// output breath_light,
	output init_calib_complete
 );
 
wire sys_rst = 1'b0;
wire clk_200m,locked;

clk_wiz_0 clk_wiz_inst(				// 这里就是我的PLL的实例
	.clk_out1(clk_200m),
	.reset(sys_rst),
	.locked(locked),
	.clk_in1(sysclk_i)
);
 
localparam ADDR_WIDTH = 29; 		// 这几个参数要根据实际的DDR3情况来选择
localparam DATA_WIDTH = 32;			// 两片ddr3 32bit		
localparam PAYLOAD_WIDTH = DATA_WIDTH;
localparam BURST_LENGTH = 8;
localparam APP_DATA_WIDTH = 256;
localparam APP_MASK_WIDTH = APP_DATA_WIDTH / 8;

wire [ADDR_WIDTH-1:0] app_addr;
wire [2:0] app_cmd;
wire app_en;
wire app_rdy;
wire [APP_DATA_WIDTH-1:0] app_rd_data;

wire app_rd_data_end;
wire app_rd_data_valid;
wire [APP_DATA_WIDTH-1:0] app_wdf_data;
wire app_wdf_end;
wire app_wdf_rdy;
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;
wire app_wdf_wren;
wire [11:0] device_temp;
wire ui_clk;
wire ui_rst; 
localparam [1:0]IDLE =2'd0;
localparam [1:0]WRITE =2'd1;
localparam [1:0]WAIT =2'd2;
localparam [1:0]READ =2'd3;
localparam [2:0]CMD_WRITE =3'd0;
localparam [2:0]CMD_READ =3'd1;

localparam TEST_DATA_RANGE =24'd16777210;//部分测试	我选择了这个
//localparam TEST_DATA_RANGE =24'd1000;//部分测试
(*mark_debug = "true"*) reg [1 :0]state=0;
reg [23:0] Count_64=0;// 128M*2*16/256
reg [ADDR_WIDTH-1:0] app_addr_begin=0;
(*mark_debug = "true"*) wire tg_compare_error;
assign app_wdf_end =app_wdf_wren;//两个相等即可
assign app_en =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : ((state==READ)&&app_rdy);
assign app_wdf_wren =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : 1'b0;
assign app_cmd =(state==WRITE) ? CMD_WRITE : CMD_READ;
assign app_addr =app_addr_begin;
assign app_wdf_data ={
	 
	Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
	 
	Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
	 
	Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
	 
	Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0]
};//写入的数据是计数器

always@(posedge ui_clk)begin
	if(ui_rst&!init_calib_complete) begin
		state <=IDLE;
		app_addr_begin <=28'd0;
		Count_64 <=24'd0;
	end
	else case(state)
	IDLE: begin
		state <=WRITE;
		if(app_addr_begin >= 24'd16777210)
		app_addr_begin <=28'd0;
		Count_64 <=24'd0;
	end
	WRITE: begin//写整片的 DDR3
		state <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy ? WAIT:state;//最后一个地址写完之后跳出状态
		Count_64 <=app_rdy&&app_wdf_rdy?(Count_64+24'd1):Count_64; 
		app_addr_begin <=app_rdy&&app_wdf_rdy?(app_addr_begin+28'd8):app_addr_begin;//跳到下一个(8*32=256)bit 数据地址
	end
	WAIT: begin
		state <=READ;
		Count_64 <=24'd0; 
		app_addr_begin <=28'd0; 
	end
	READ: begin//读整片的 DDR3
		state <=(Count_64==TEST_DATA_RANGE)&&app_rdy? IDLE:state;
		Count_64 <=app_rdy?(Count_64+24'd1):Count_64; 
		app_addr_begin <=app_rdy?(app_addr_begin+28'd8):app_addr_begin;
	end
	default:begin
		state <=IDLE;
		app_addr_begin <=28'd0;
		Count_64 <=24'd0;
	end 
	endcase
end

(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [63:0]app_rd_data_r =0;
(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg app_rd_data_valid_r =0;
 
always @(posedge ui_clk) begin
	app_rd_data_r <= app_rd_data[63:0];
	app_rd_data_valid_r <= app_rd_data_valid;
end
 
//16bit count used for comparation
reg [7:0] count_temp =0;
always @(posedge ui_clk) begin
 if(app_rd_data_valid_r)
	count_temp<= count_temp + 1'b1;
 else if(state==WAIT)
	count_temp <= 8'd0;
end

//compare data read from mig
(*mark_debug = "true"*) wire [63:0]cm_data;
assign cm_data = {count_temp,count_temp,count_temp,count_temp,count_temp,count_temp,count_temp,count_temp};
assign tg_compare_error=(app_rd_data_valid_r&&(cm_data!=app_rd_data_r));

mig_7series_0 u_mig_7series_0 (
 // Memory interface ports
 .ddr3_addr (ddr3_addr), // output [13:0] ddr3_addr
 .ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
 .ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
 .ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
 .ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
 .ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
 .ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
 .ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
 .ddr3_we_n (ddr3_we_n), // output ddr3_we_n
 .ddr3_dq (ddr3_dq), // inout [31:0] ddr3_dq
 .ddr3_dqs_n (ddr3_dqs_n), // inout [3:0] ddr3_dqs_n
 .ddr3_dqs_p (ddr3_dqs_p), // inout [3:0] ddr3_dqs_p
 .ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n
 .ddr3_dm (ddr3_dm), // output [3:0] ddr3_dm
 .ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
 .init_calib_complete (init_calib_complete), // output init_calib_complete
 // Application interface ports
 .app_addr (app_addr), // input [27:0] app_addr
 .app_cmd (app_cmd), // input [2:0] app_cmd
 .app_en (app_en), // input app_en
 .app_wdf_data (app_wdf_data), // input [255:0] app_wdf_data
 .app_wdf_end (app_wdf_end), // input app_wdf_end
 .app_wdf_wren (app_wdf_wren), // input app_wdf_wren
 .app_rd_data (app_rd_data), // output [255:0] app_rd_data
 .app_rd_data_end (app_rd_data_end), // output app_rd_data_end
 .app_rd_data_valid (app_rd_data_valid), // output app_rd_data_valid
 .app_rdy (app_rdy), // output app_rdy
 .app_wdf_rdy (app_wdf_rdy), // output app_wdf_rdy
 .app_sr_req (1'b0),
 .app_ref_req (1'b0),
 .app_zq_req (1'b0),
 .app_sr_active (app_sr_active), // output app_sr_active
 .app_ref_ack (app_ref_ack), // output app_ref_ack
 .app_zq_ack (app_zq_ack), // output app_zq_ack
 .ui_clk (ui_clk), // output ui_clk
 .ui_clk_sync_rst (ui_rst), // output ui_clk_sync_rst
 .app_wdf_mask (32'd0), // input [31:0] app_wdf_mask
 // System Clock Ports
 .sys_clk_i (clk_200m),
 .sys_rst (locked), // input sys_rst
 .device_temp (device_temp)
 );
endmodule

搞完代码之后,就是要做一个约束文件,把自己主时钟脚(50Mhz),系统复位脚,错误灯等脚分配好。
米联客的代码里面支持ILA在线仿真,我下面就截几张图放出来,告诉大家怎么设置仿真。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

下面再放几张自己的板子测下来的情况,和教程的上面的波形基本一致。至此,ZYNQ PL侧挂载两片DDR3的步骤都已经说完了。
在这里插入图片描述
在这里插入图片描述

  • 9
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
Zynq 7020内部集成了一个FPGA(Field Programmable Gate Array),用于实现可编程逻辑(PL)部分。 FPGA是一种可编程逻辑器件,可以通过编程来实现各种数字电路功能。在Zynq 7020中,PL部分可以与PS(Processing System)部分进行高效交互,通过AXI4协议来实现数据传输。开发人员可以使用Vivado软件进行FPGA的配置和调试。 Zynq 7020配套的开发板为Zybo Z7系列,该开发板上搭载了Zynq 7020芯片,并且具有相应的硬件配置。 总结来说,Zynq 7020内部集成了一个FPGA,可以通过Vivado进行配置和调试。在开发过程中,可以使用Zybo Z7系列开发板进行硬件开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ZYNQ AX7020 PL读写PS端 DDR 数据 vivado](https://download.csdn.net/download/Calvin790704/87389599)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于FPGA的DDS在Vivado中仿真以及在ZYNQ7020上板的实现(1)](https://blog.csdn.net/syyzuiqiang/article/details/118103211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值