vivado中,有三种方式可以实现PCIE功能:
PCIE IP核所做的工作主要有两点,一个是将TLP包转换成AXI协议;另一个是支持DMA操作。
一,调用7 Series Integrated Block for PCI Express IP核,这是最基础的PCIE IP核,比较复杂。
1,在IP Catalog找到XDMA,使用简化设置
2,通道数选择4,AXI数据位宽选择128位,DMA Interface选择AXI Memory Mapped,PCIE参考时钟100MHz,AXI时钟125MHz。
3,设置PCIE的BAR空间,勾中PCIe to AXI-Lite Master Interface,选择Size,然后一路默认生成好IP核。IP核生成完成后,右键选择Open IP Example Design,进入Example Design项目。在xdc中约束PCIE的参考时钟和复位引脚的管脚和IO电平后,点击Generate Bitstream,等待bit文件生成。解压xdma_driver_win_installers_x64_2018_2.zip,选择的Win版本XDMADriverInstaller.msi安装。过程中可能会提醒驱动没有正确的数字签名,是否安装,点击是。
二,调用AXI Memory Mapped To PCI Express IP核,对7 Series Integrated Block for PCI Express进一步封装,使用Example Design直接运行,但需要添加DMA IP核实现DMA数据传输。
1,PCIE basics
2,PCIE link config
3,PCIE ID
4,PCIE BARS
5,PCIE misc
6,AXI BARS
7,AXI system
`timescale 1 ps / 1 ps
module zc706_pcie_wrapper
(DDR_addr,
DDR_ba,
DDR_cas_n,
DDR_ck_n,
DDR_ck_p,
DDR_cke,
DDR_cs_n,
DDR_dm,
DDR_dq,
DDR_dqs_n,
DDR_dqs_p,
DDR_odt,
DDR_ras_n,
DDR_reset_n,
DDR_we_n,
FIXED_IO_ddr_vrn,
FIXED_IO_ddr_vrp,
FIXED_IO_mio,
FIXED_IO_ps_clk,
FIXED_IO_ps_porb,
FIXED_IO_ps_srstb,
mmcm_lock,
pcie_7x_mgt_rxn,
pcie_7x_mgt_rxp,
pcie_7x_mgt_txn,
pcie_7x_mgt_txp,
perst_n,
ref_clk_clk_n,
ref_clk_clk_p);
inout [14:0]DDR_addr;
inout [2:0]DDR_ba;
inout DDR_cas_n;
inout DDR_ck_n;
inout DDR_ck_p;
inout DDR_cke;
inout DDR_cs_n;
inout [3:0]DDR_dm;
inout [31:0]DDR_dq;
inout [3:0]DDR_dqs_n;
inout [3:0]DDR_dqs_p;
inout DDR_odt;
inout DDR_ras_n;
inout DDR_reset_n;
inout DDR_we_n;
inout FIXED_IO_ddr_vrn;
inout FIXED_IO_ddr_vrp;
inout [53:0]FIXED_IO_mio;
inout FIXED_IO_ps_clk;
inout FIXED_IO_ps_porb;
inout FIXED_IO_ps_srstb;
output mmcm_lock;
input [3:0]pcie_7x_mgt_rxn;
input [3:0]pcie_7x_mgt_rxp;
output [3:0]pcie_7x_mgt_txn;
output [3:0]pcie_7x_mgt_txp;
input perst_n;
input [0:0]ref_clk_clk_n;
input [0:0]ref_clk_clk_p;
wire [14:0]DDR_addr;
wire [2:0]DDR_ba;
wire DDR_cas_n;
wire DDR_ck_n;
wire DDR_ck_p;
wire DDR_cke;
wire DDR_cs_n;
wire [3:0]DDR_dm;
wire [31:0]DDR_dq;
wire [3:0]DDR_dqs_n;
wire [3:0]DDR_dqs_p;
wire DDR_odt;
wire DDR_ras_n;
wire DDR_reset_n;
wire DDR_we_n;
wire FIXED_IO_ddr_vrn;
wire FIXED_IO_ddr_vrp;
wire [53:0]FIXED_IO_mio;
wire FIXED_IO_ps_clk;
wire FIXED_IO_ps_porb;
wire FIXED_IO_ps_srstb;
wire mmcm_lock;
wire [3:0]pcie_7x_mgt_rxn;
wire [3:0]pcie_7x_mgt_rxp;
wire [3:0]pcie_7x_mgt_txn;
wire [3:0]pcie_7x_mgt_txp;
wire perst_n;
wire [0:0]ref_clk_clk_n;
wire [0:0]ref_clk_clk_p;
zc706_pcie zc706_pcie_i
(.DDR_addr(DDR_addr),
.DDR_ba(DDR_ba),
.DDR_cas_n(DDR_cas_n),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cke(DDR_cke),
.DDR_cs_n(DDR_cs_n),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_reset_n(DDR_reset_n),
.DDR_we_n(DDR_we_n),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
.mmcm_lock(mmcm_lock),
.pcie_7x_mgt_rxn(pcie_7x_mgt_rxn),
.pcie_7x_mgt_rxp(pcie_7x_mgt_rxp),
.pcie_7x_mgt_txn(pcie_7x_mgt_txn),
.pcie_7x_mgt_txp(pcie_7x_mgt_txp),
.perst_n(perst_n),
.ref_clk_clk_n(ref_clk_clk_n),
.ref_clk_clk_p(ref_clk_clk_p));
endmodule
管脚约束:
#GPIO LEDs
set_property PACKAGE_PIN A17 [get_ports mmcm_lock]
set_property IOSTANDARD LVCMOS18 [get_ports mmcm_lock]
# PCI Express reset (perst)
set_property PACKAGE_PIN AK23 [get_ports perst_n]
set_property IOSTANDARD LVCMOS18 [get_ports perst_n]
# PCI Express reference clock 100MHz
set_property PACKAGE_PIN N8 [get_ports {ref_clk_clk_p[0]}]
set_property PACKAGE_PIN N7 [get_ports {ref_clk_clk_n[0]}]
create_clock -period 10.000 -name ref_clk_clk_p -waveform {0.000 5.000} [get_ports ref_clk_clk_p]
# MGT locations
set_property PACKAGE_PIN P5 [get_ports {pcie_7x_mgt_rxn[0]}]
set_property PACKAGE_PIN P6 [get_ports {pcie_7x_mgt_rxp[0]}]
set_property PACKAGE_PIN N3 [get_ports {pcie_7x_mgt_txn[0]}]
set_property PACKAGE_PIN N4 [get_ports {pcie_7x_mgt_txp[0]}]
set_property PACKAGE_PIN T5 [get_ports {pcie_7x_mgt_rxn[1]}]
set_property PACKAGE_PIN T6 [get_ports {pcie_7x_mgt_rxp[1]}]
set_property PACKAGE_PIN P1 [get_ports {pcie_7x_mgt_txn[1]}]
set_property PACKAGE_PIN P2 [get_ports {pcie_7x_mgt_txp[1]}]
set_property PACKAGE_PIN U3 [get_ports {pcie_7x_mgt_rxn[2]}]
set_property PACKAGE_PIN U4 [get_ports {pcie_7x_mgt_rxp[2]}]
set_property PACKAGE_PIN R3 [get_ports {pcie_7x_mgt_txn[2]}]
set_property PACKAGE_PIN R4 [get_ports {pcie_7x_mgt_txp[2]}]
set_property PACKAGE_PIN V5 [get_ports {pcie_7x_mgt_rxn[3]}]
set_property PACKAGE_PIN V6 [get_ports {pcie_7x_mgt_rxp[3]}]
set_property PACKAGE_PIN T1 [get_ports {pcie_7x_mgt_txn[3]}]
set_property PACKAGE_PIN T2 [get_ports {pcie_7x_mgt_txp[3]}]
SDK测试:
#include "xparameters.h" /* Defines for XPAR constants */
#include "xaxipcie.h" /* XAxiPcie level 1 interface */
#include "stdio.h"
#include "xil_printf.h"
/************************** Constant Definitions ****************************/
#define AXIPCIE_DEVICE_ID XPAR_AXIPCIE_0_DEVICE_ID
#define PCIE_CFG_CMD_IO_EN 0x00000001 /* I/O access enable */
#define PCIE_CFG_CMD_MEM_EN 0x00000002 /* Memory access enable */
#define PCIE_CFG_CMD_BUSM_EN 0x00000004 /* Bus master enable */
#define PCIE_CFG_CMD_PARITY 0x00000040 /* parity errors response */
#define PCIE_CFG_CMD_SERR_EN 0x00000100 /* SERR report enable */
//PCIe Configuration registers offsets
#define PCIE_CFG_ID_REG 0x0000 /* Vendor ID/Device ID offset */
#define PCIE_CFG_CMD_STATUS_REG 0x0001 /*
* Command/Status Register
* Offset
*/
#define PCIE_CFG_PRI_SEC_BUS_REG 0x0006 /*
* Primary/Sec.Bus Register
* Offset
*/
#define PCIE_CFG_CAH_LAT_HD_REG 0x0003 /*
* Cache Line/Latency Timer/
* Header Type/
* BIST Register Offset
*/
#define PCIE_CFG_BAR_0_REG 0x0004 /* PCIe Base Addr 0 */
#define PCIE_CFG_FUN_NOT_IMP_MASK 0xFFFF
#define PCIE_CFG_HEADER_TYPE_MASK 0x00EF0000
#define PCIE_CFG_MUL_FUN_DEV_MASK 0x00800000
#define PCIE_CFG_MAX_NUM_OF_BUS 256
#define PCIE_CFG_MAX_NUM_OF_DEV 1
#define PCIE_CFG_MAX_NUM_OF_FUN 8
#define PCIE_CFG_PRIM_SEC_BUS 0x00070100
#define PCIE_CFG_HEADER_O_TYPE 0x0000
#define PCIE_CFG_BAR_0_ADDR 0x00001111
// Macros for reading link speed and width from the core
#define XAxiPcie_IsGen3(InstancePtr) \
(XAxiPcie_ReadReg((InstancePtr)->Config.BaseAddress, \
XAXIPCIE_PHYSC_OFFSET) & 0x00001000) ? 1 : 0
#define XAxiPcie_IsGen2(InstancePtr) \
(XAxiPcie_ReadReg((InstancePtr)->Config.BaseAddress, \
XAXIPCIE_PHYSC_OFFSET) & 0x00000001) ? 1 : 0
#define XAxiPcie_LinkWidth(InstancePtr) \
((XAxiPcie_ReadReg((InstancePtr)->Config.BaseAddress, \
XAXIPCIE_PHYSC_OFFSET) & XAXIPCIE_PHYSC_LINK_WIDTH_MASK) >> 1)
/**************************** Type Definitions ******************************/
/***************** Macros (Inline Functions) Definitions ********************/
/************************** Function Prototypes *****************************/
int PcieInitRootComplex(XAxiPcie *AxiPciePtr, u16 DeviceId);
void PCIeEnumerateFabric(XAxiPcie *AxiPciePtr);
static void __attribute__ ((noinline)) UtilDelay(unsigned int Seconds);
/************************** Variable Definitions ****************************/
/* Allocate PCIe Root Complex IP Instance */
XAxiPcie AxiPcieInstance;
/****************************************************************************/
/**
* This function is the entry point for PCIe Root Complex Enumeration Example
* @return - XST_SUCCESS if successful
* - XST_FAILURE if unsuccessful.
*
* @note None.
*
*****************************************************************************/
int main(void)
{
int Status;
// Allow time for link-up
UtilDelay(1);
xil_printf("=============================\r\n");
xil_printf("PCIe Gen2 Enumeration Example\r\n");
xil_printf("=============================\r\n");
/* Initialize Root Complex */
Status = PcieInitRootComplex(&AxiPcieInstance, AXIPCIE_DEVICE_ID);
if (Status != XST_SUCCESS)
{
xil_printf("Failed to initialize AXI PCIe Root port\r\n");
return XST_FAILURE;
}
/* Scan PCIe Fabric */
PCIeEnumerateFabric(&AxiPcieInstance);
return XST_SUCCESS;
}
/****************************************************************************/
/**
* This function returns the negotiated PCIe link speed once link-up is achieved
* @param AxiPciePtr is a pointer to an instance of XAxiPcie data
* structure represents a root complex IP.
* @return - 1 if Gen1
* - 2 if Gen2
* - 3 if Gen3
* - 0 if unsuccessful.
*
* @note None.
******************************************************************************/
int get_pcie_link_speed(XAxiPcie *AxiPciePtr)
{
int is_gen2;
int is_gen3;
is_gen2 = XAxiPcie_IsGen2(AxiPciePtr);
is_gen3 = XAxiPcie_IsGen3(AxiPciePtr);
if((is_gen2 == 0) && (is_gen3 == 1))
return(3);
if((is_gen2 == 1) && (is_gen3 == 0))
return(2);
if((is_gen2 == 0) && (is_gen3 == 0))
return(1);
return(0);
}
/****************************************************************************/
/**
* This function returns the negotiated PCIe link width once link-up is achieved
* @param AxiPciePtr is a pointer to an instance of XAxiPcie data
* structure represents a root complex IP.
* @return - link width (1,2,4 or 8)
* @note None.
******************************************************************************/
int get_pcie_link_width(XAxiPcie *AxiPciePtr)
{
int i;
int link_width;
int result;
link_width = XAxiPcie_LinkWidth(AxiPciePtr);
result = 1;
for(i = 0; i < link_width; i++)
result = result * 2;
return(result);
}
/****************************************************************************/
/**
* This function initializes a AXI PCIe IP built as a root complex
* @param AxiPciePtr is a pointer to an instance of XAxiPcie data
* structure represents a root complex IP.
* @param DeviceId is AXI PCIe IP unique ID
* @return - XST_SUCCESS if successful.
* - XST_FAILURE if unsuccessful.
* @note None.
******************************************************************************/
int PcieInitRootComplex(XAxiPcie *AxiPciePtr, u16 DeviceId)
{
int Status;
u32 HeaderData;
u32 InterruptMask;
u8 BusNumber;
u8 DeviceNumber;
u8 FunNumber;
u8 PortNumber;
XAxiPcie_Config *ConfigPtr;
ConfigPtr = XAxiPcie_LookupConfig(DeviceId);
Status = XAxiPcie_CfgInitialize(AxiPciePtr, ConfigPtr,ConfigPtr->BaseAddress);
if (Status != XST_SUCCESS)
{
xil_printf("Failed to initialize PCIe Root Complex""IP Instance\r\n");
return XST_FAILURE;
}
if(!AxiPciePtr->Config.IncludeRootComplex)
{
xil_printf("Failed to initialize...AXI PCIE is configured"" as endpoint\r\n");
return XST_FAILURE;
}
/* Make sure link is up. */
Status = XAxiPcie_IsLinkUp(AxiPciePtr);
if (Status != TRUE )
{
xil_printf("Link:\r\n - LINK NOT UP!\r\n");
return XST_FAILURE;
}
xil_printf("Link:\r\n - LINK UP, Gen%d x%d lanes\r\n",
get_pcie_link_speed(AxiPciePtr),get_pcie_link_width(AxiPciePtr));
xil_printf("Interrupts:\r\n");
/* See what interrupts are currently enabled */
XAxiPcie_GetEnabledInterrupts(AxiPciePtr, &InterruptMask);
xil_printf(" - currently enabled: %8X\r\n", InterruptMask);
/* Make sure all interrupts disabled. */
XAxiPcie_DisableInterrupts(AxiPciePtr, XAXIPCIE_IM_ENABLE_ALL_MASK);
/* See what interrupts are currently pending */
XAxiPcie_GetPendingInterrupts(AxiPciePtr, &InterruptMask);
xil_printf(" - currently pending: %8X\r\n", InterruptMask);
/* Just if there is any pending interrupt then clear it.*/
XAxiPcie_ClearPendingInterrupts(AxiPciePtr,
XAXIPCIE_ID_CLEAR_ALL_MASK);
/ * Read enabled interrupts and pending interrupts
* to verify the previous two operations and also
* to test those two API functions
*/
xil_printf("Cleared pending interrupts:\r\n");
XAxiPcie_GetEnabledInterrupts(AxiPciePtr, &InterruptMask);
xil_printf(" - currently enabled: %8X\r\n", InterruptMask);
XAxiPcie_GetPendingInterrupts(AxiPciePtr, &InterruptMask);
xil_printf(" - currently pending: %8X\r\n", InterruptMask);
/*
* Read back requester ID.
*/
XAxiPcie_GetRequesterId(AxiPciePtr, &BusNumber,
&DeviceNumber, &FunNumber, &PortNumber);
xil_printf("Requester ID:\r\n");
xil_printf(" - Bus Number: %02X\r\n"
" - Device Number: %02X\r\n"
" - Function Number: %02X\r\n"
" - Port Number: %02X\r\n",
BusNumber, DeviceNumber, FunNumber, PortNumber);
/* Set up the PCIe header of this Root Complex */
XAxiPcie_ReadLocalConfigSpace(AxiPciePtr,
PCIE_CFG_CMD_STATUS_REG, &HeaderData);
HeaderData |= (PCIE_CFG_CMD_BUSM_EN | PCIE_CFG_CMD_MEM_EN |
PCIE_CFG_CMD_IO_EN | PCIE_CFG_CMD_PARITY |
PCIE_CFG_CMD_SERR_EN);
XAxiPcie_WriteLocalConfigSpace(AxiPciePtr,
PCIE_CFG_CMD_STATUS_REG, HeaderData);
/*
* Read back local config reg.
* to verify the write.
*/
xil_printf("PCIe Local Config Space:\r\n");
XAxiPcie_ReadLocalConfigSpace(AxiPciePtr,
PCIE_CFG_CMD_STATUS_REG, &HeaderData);
xil_printf(" - %8X at register CommandStatus\r\n", HeaderData);
/*
* Set up Bus number
*/
HeaderData = PCIE_CFG_PRIM_SEC_BUS;
XAxiPcie_WriteLocalConfigSpace(AxiPciePtr,
PCIE_CFG_PRI_SEC_BUS_REG, HeaderData);
/*
* Read back local config reg.
* to verify the write.
*/
XAxiPcie_ReadLocalConfigSpace(AxiPciePtr,
PCIE_CFG_PRI_SEC_BUS_REG, &HeaderData);
xil_printf(" - %8X at register Prim Sec. Bus\r\n", HeaderData);
/* Now it is ready to function */
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function enumerates its PCIe system and figures out the nature of each
* component there like end points,bridges,...
*
* @param AxiPciePtr is a pointer to an instance of XAxiPcie
* data structure represents a root complex IP.
*
* @return None.
*
* @note None.
*
******************************************************************************/
void PCIeEnumerateFabric(XAxiPcie *AxiPciePtr)
{
u32 ConfigData;
u32 PCIeHeaderType;
u32 PCIeMultiFun;
u32 PCIeBusNum;
u32 PCIeDevNum;
u32 PCIeFunNum;
u16 PCIeVendorID;
u32 RegVal;
xil_printf("Enumeration of PCIe Fabric:\r\n");
/* Scan PCIe Fabric */
for (PCIeBusNum = 0; PCIeBusNum < PCIE_CFG_MAX_NUM_OF_BUS;
PCIeBusNum++) {
for (PCIeDevNum = 0; PCIeDevNum < PCIE_CFG_MAX_NUM_OF_DEV;
PCIeDevNum++) {
for (PCIeFunNum = 0;
PCIeFunNum < PCIE_CFG_MAX_NUM_OF_FUN;
PCIeFunNum++) {
/* Vendor ID */
XAxiPcie_ReadRemoteConfigSpace(
AxiPciePtr,PCIeBusNum,
PCIeDevNum, PCIeFunNum,
PCIE_CFG_ID_REG, &ConfigData);
PCIeVendorID = (u16) (ConfigData & 0xFFFF);
if (PCIeVendorID ==
PCIE_CFG_FUN_NOT_IMP_MASK) {
if (PCIeFunNum == 0)
/*
* We don't need to look
* any further on this device.
*/
break;
}
else {
xil_printf("PCIeBus %02X:\r\n"
" - PCIeDev: %02X\r\n"
" - PCIeFunc: %02X\r\n",
PCIeBusNum, PCIeDevNum,
PCIeFunNum);
xil_printf(" - Vendor ID: %04X \r\n",
PCIeVendorID);
/* Header Type */
XAxiPcie_ReadRemoteConfigSpace(
AxiPciePtr, PCIeBusNum,
PCIeDevNum, PCIeFunNum,
PCIE_CFG_CAH_LAT_HD_REG,
&ConfigData);
PCIeHeaderType = ConfigData &
PCIE_CFG_HEADER_TYPE_MASK;
PCIeMultiFun = ConfigData &
PCIE_CFG_MUL_FUN_DEV_MASK;
if (PCIeHeaderType ==
PCIE_CFG_HEADER_O_TYPE) {
/* This is an End Point */
xil_printf(" - End Point\r\n");
/*
* Initialize this end point
* and return.
*/
XAxiPcie_ReadRemoteConfigSpace(
AxiPciePtr,
PCIeBusNum, PCIeDevNum,
PCIeFunNum,
PCIE_CFG_CMD_STATUS_REG,
&ConfigData);
ConfigData |=
(PCIE_CFG_CMD_BUSM_EN |
PCIE_CFG_CMD_MEM_EN);
XAxiPcie_WriteRemoteConfigSpace
(AxiPciePtr,
PCIeBusNum, PCIeDevNum,
PCIeFunNum,
PCIE_CFG_CMD_STATUS_REG,
ConfigData);
/*
* Write Address to
* PCIe BAR0
*/
ConfigData =
(PCIE_CFG_BAR_0_ADDR |
PCIeBusNum |
PCIeDevNum |
PCIeFunNum);
XAxiPcie_WriteRemoteConfigSpace
(AxiPciePtr,
PCIeBusNum, PCIeDevNum,
PCIeFunNum, PCIE_CFG_BAR_0_REG,
ConfigData);
xil_printf(" - End Point has been"
" enabled\r\n");
}
else {
/* This is a bridge */
xil_printf(" - Bridge\r\n");
}
}
if ((!PCIeFunNum) && (!PCIeMultiFun)) {
/*
* If it is function 0 and it is not a
* multi function device, we don't need
* to look any further on this devie
*/
break;
}
} /* Functions in one device */
} /* Devices on the same bus */
} /* Buses in the same system */
xil_printf("End of Enumeration\r\n");
/* Bridge enable */
XAxiPcie_GetRootPortStatusCtrl(AxiPciePtr, &RegVal);
RegVal |= XAXIPCIE_RPSC_BRIDGE_ENABLE_MASK;
XAxiPcie_SetRootPortStatusCtrl(AxiPciePtr, RegVal);
return;
}
static void __attribute__ ((noinline)) UtilDelay(unsigned int Seconds)
{
#if defined (__MICROBLAZE__) || defined(__PPC__)
static int WarningFlag = 0;
if (((mfmsr() & 0x20) == 0) && (!WarningFlag))
{
WarningFlag = 1;
}
#define ITERS_PER_SEC (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 6)
asm volatile ("\n"
"1: \n\t"
"addik r7, r0, %0 \n\t"
"2: \n\t"
"addik r7, r7, -1 \n\t"
"bneid r7, 2b \n\t"
"or r0, r0, r0 \n\t"
"bneid %1, 1b \n\t"
"addik %1, %1, -1 \n\t"
:: "i"(ITERS_PER_SEC), "d" (Seconds));
#else
sleep(Seconds);
#endif
}
三,调用DMA/Bridge Subsystem for PCI Express (PCIe) IP核,别名XDMA,对PCIE和DMA一起进行了封装,也可以直接使用Example Design直接运行。
四,发送和接收
1,TLP FPGA 发送端(FPGA 应答主机的 TLP 包)
2,TLP FPGA 接收端(FPGA 接收主机的 TLP 包)