天天看點

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

ZYNQ 7020 FPGA DDR3

  • ZYNQ是FPGA+雙核ARM的架構

ZYNQ是FPGA+雙核ARM的架構

最近我需要用到ZYNQ來做東西,但是網上看見到的開發闆在FPGA部分沒有沒有挂DDR3,隻能結合自己以前的經驗和現有的資料,摸索一下。本文分為兩部分:

1. zynq的FPGA部分怎麼加上兩片ddr3,引腳配置設定,時鐘等。

2. 如何測試,驗證PCB效果。

  1. 注意下面的FPGA Part和速度等級,確定選擇的是自己使用的晶片,之後選擇下一步。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖1
  2. 下一步。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖2
  3. 這裡有三個注意:

    3.1我使用的是XC7Z020CLG484-2,最大支援DDR3時鐘是400Mhz。

    3.2我用的DDR3型号mt41k256m16tw-107it:p,找不到,隻能選擇相近的型号,但是都是相容的。

    3.3我是兩片ddr3,共計32bit,我見有些人用一片,那這裡就是16。

    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖3

4.重點來了,這裡的選擇卡了我很久,因為ZYNQ7020的時鐘是50Mhz,我不知道這裡是填200Mhz還是選擇50Mhz,最後看到米聯客的講解,我用一個PLL,50Mhz轉200Mhz做MIG的時鐘輸入不久行了嘛,是以此處果斷填200Mhz.(另外如果此處不填200Mhz,圖6中的system clock選項沒有use system clock選項,見圖7)

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖5

  1. 這裡的system clock選擇no buffer,reference clock選擇use system clock。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖6
  2. 下面這個圖就是因為前面圖5沒有選擇200Mhz時鐘時,才會沒有use system clock的選項。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖7

7.選擇Fixed Pin Out,讓MIG向導幫助我們實作DDR3引腳的一些配置設定。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖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,每個都對應着一些腳,供大家選擇。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖9

9. 可以看見Pin Number選框下面對應着很多的腳,每選擇一個腳,對應就會出現一道杠,表示這個腳被用了。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖10

10.下面的一些腳配置設定,方法雷同,請各位大膽摸索,大不了重建個MIG(啊,我建了多少個,自己都忘了)

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖11

11.MIG支援你之前做好的引腳配置設定,選擇read XDC/UCF按鈕,就可以将之前做好的引腳配置設定檔案導入。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖12

12. 當你的引腳配置設定完畢之後,要用validate按鈕驗證一下,如果驗證成功之後,next按鈕才可以點選,否則是點不動的,不能進行下一步操作。當然,如果你驗證成功之後,記得點選Save Pin Out,将你自己配置設定好的引腳做一下儲存,否則當你重新打開MIG之後,你會發現,卧槽,剛剛配置設定好的咋沒有了。這種感受你最好别體驗,真難受。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖13

  1. 下一步。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖14
  2. 做了一個總結,有興趣自己看吧。下一步。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖15

15.當然是接收啦。下一步。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

|:------------------------------------😐 圖16

  1. 點選generate。恭喜你,或許你的ddr3引腳配置設定完成啦。導入到自己的原理圖裡面吧。
    ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
    |:------------------------------------😐 圖17

17.在你的工程裡面會生成一個MIG 的IP核。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

下面就是驗證用的代碼部分,代碼的來源是米聯客的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 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構

下面再放幾張自己的闆子測下來的情況,和教程的上面的波形基本一緻。至此,ZYNQ PL側挂載兩片DDR3的步驟都已經說完了。

ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構
ZYNQ 7020 FPGA DDR3ZYNQ是FPGA+雙核ARM的架構