/****************************************************************************\
 *     Copyright (C) 2011, 2012, 2013 Texas Instruments Incorporated.       *
 *                           All Rights Reserved                            *
 *                                                                          *
 * GENERAL DISCLAIMER                                                       *
 * -------------------------------------------------------------------      *
 * All software and related documentation is provided "AS IS" and without   *
 * warranty or support of any kind and Texas Instruments expressly disclaims*
 * all other warranties, express or implied, including, but not limited to, *
 * the implied warranties of merchantability and fitness for a particular   *
 * purpose.  Under no circumstances shall Texas Instruments be liable for   *
 * any incidental, special or consequential damages that result from the    *
 * use or inability to use the software or related documentation, even if   *
 * Texas Instruments has been advised of the liability.                     *
 ****************************************************************************
Example to show memory access and interrupt throughput PCIE
In this example, Device1's memory are mapped to Device0 through PCIE  
so, Device0 accesses Device 1 through the PCIE memory window just like 
access other memory space. 
To run 2 Device test, You should run this project on the second core of the second 
Device firstly, and then the first core of the first Device
Internal loopback is also supported in this example, for this case, 
Device0 actually access its own local memory through PCIE memory window.
 ****************************************************************************
 * Written by :                                                             *
 *            Brighton Feng                                                 *
 *            Texas Instruments                                             *
 *            June 23, 2013                                                 *
 *  December 29, 2013 Thomas Yang,  add two Devices test
 ***************************************************************************/
#include <stdio.h>
#include <string.h>
#include "K2_common.h"
#include "common_test.h"
#include "K2_Board_Init.h"
#include "K2_PCIE_init_drv.h"
#include "PCIE_intc.h"
#include "PCIE_test.h"

#define TESTED_PCIE_INSTANT_NUM 	0

PCIE_Loopback_Mode loop_mode= PCIE_PHY_LOOPBACK;
PCIE_Address_Width address_width= PCIE_ADDRESS_32_BITS;

/*first outbound region is for configuration for this test,
data window start from the second outbound region*/
#if (TESTED_PCIE_INSTANT_NUM==0)
#ifdef CSL_PCIE_0_SLV_DATA
#define CSL_PCIE_DATA_BASE CSL_PCIE_0_SLV_DATA
#else
#define CSL_PCIE_DATA_BASE CSL_PCIE_SLV_DATA
#endif
#else
#define CSL_PCIE_DATA_BASE CSL_PCIE_1_SLV_DATA
#endif

#define PCIE_OUTBOUND_DATA_WINDOW 	(CSL_PCIE_DATA_BASE+0x800000)

//comment below definition if DDR3B is not availible
//#define DDRB_TEST 	1

#define PCIE_PREFETCH_BASE_ADDRESS 	0x80000000
#define PCIE_NONFETCH_BASE_ADDRESS 	0x10000000

/*RC BAR0 directly map to RC's PCIE application registers,
EP may write this space to trigger MSI to RC*/
#define PCIE_RC_BAR0_ADDRESS 		0x01000000 	//should >=0x400000???

//PCIE remote test address
PCIERemoteTestAddress PcieRemoteTestAddr __attribute__ ((section (".far.PCIEInit")))=
{
	PCIE_OUTBOUND_DATA_WINDOW+0x0000000,//DDRA_SRC_ADDR;
	PCIE_OUTBOUND_DATA_WINDOW+0x1000000,//DDRA_DST_ADDR;
#ifdef DDRB_TEST
	PCIE_OUTBOUND_DATA_WINDOW+0x2000000,//DDRB_SRC_ADDR;
	PCIE_OUTBOUND_DATA_WINDOW+0x3000000,//DDRB_DST_ADDR;
#else
	0,//DDRB_SRC_ADDR;
	0,//DDRB_DST_ADDR;
#endif
	PCIE_OUTBOUND_DATA_WINDOW+0x4000000,//MSMC_RAM_SRC_ADDR;
	PCIE_OUTBOUND_DATA_WINDOW+0x4040000,//MSMC_RAM_DST_ADDR;
	PCIE_OUTBOUND_DATA_WINDOW+0x4100000,//LL2_SRC_ADDR;
	PCIE_OUTBOUND_DATA_WINDOW+0x4120000,//LL2_DST_ADDR;
};

/*Used for inbound configuration, as inbound offset*/
PCIE_Memory_Region memory_regions[] __attribute__ ((section (".far.PCIEInit")))=
{
	{0x90000000, 32*1024*1024}, //DDR3A
#ifdef DDRB_TEST
	{0x70000000, 32*1024*1024},  //DDR3B
#else
	{0xA0000000, 32*1024*1024},  //DDR3A
#endif
	{0x0C100000, 1*1024*1024}, 	//MSMC_RAM
	{0x10820000, 256*1024} 	//LL2
};

/*outbound PCIE address, only used for 2 Device test for EP to access RC*/
unsigned long long EP_OB_PCIE_address[] __attribute__ ((section (".far.PCIEInit")))=
{//EP OB configuration should be done manually
	PCIE_RC_BAR0_ADDRESS,   
	0x0000000080000000,
	0x0000000090000000
	//to do
};

PCIE_Memory_Regions prefetch_regions __attribute__ ((section (".far.PCIEInit"))); 

PCIE_Memory_Regions nonfetch_regions __attribute__ ((section (".far.PCIEInit")));

PCIE_Inbound_Memory_Regions inbound_memory_regions __attribute__ ((section (".far.PCIEInit")));

PCIE_Outbound_Memory_Regions outbound_memory_regions __attribute__ ((section (".far.PCIEInit")));

PCIE_RC_Config rc_cfg __attribute__ ((section (".far.PCIEInit")));

PCIE_Interrupt_Config PCIE_int_cfg __attribute__ ((section (".far.PCIEInit")));

PCIE_Remote_CFG_SETUP remote_cfg_setup __attribute__ ((section (".far.PCIEInit")));

K2_PCIE_Config PCIE_cfg __attribute__ ((section (".far.PCIEInit")));

K2_PCIE_SerdesConfig serdes_cfg __attribute__ ((section (".far.PCIEInit")));

//MMU memory ranges tables
MMU_Memory_Map_Range memory_ranges[]=
{
	{//MSMC RAM
    .uiVirtualAddress   = 0x0C000000,
    .ullPhysicalAddress = 0x0C000000,
    .uiByteCnt          = MSMC_RAM_SIZE_BYTES,
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_X,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	},                     
	{//DDR3A               
    .uiVirtualAddress   = 0x80000000,
    .ullPhysicalAddress = 0x800000000ULL, 	
#if (DDR3A_SIZE_BYTES>0x80000000ULL)
    .uiByteCnt          = 0x80000000, 	
#else
    .uiByteCnt          = DDR3A_SIZE_BYTES, 	
#endif
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#if (2==CSL_DDR3_PER_CNT)
	,{//DDR3B
    .uiVirtualAddress   = 0x60000000,
    .ullPhysicalAddress = 0x60000000, 	
    .uiByteCnt          = 0x20000000,
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_INNER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#endif
	,{//PCIE
    .uiVirtualAddress   = CSL_PCIE_DATA_BASE,
    .ullPhysicalAddress = CSL_PCIE_DATA_BASE,
    .uiByteCnt          = 0x10000000,
    .attribute          = MMU_MEM_ATTR_DEVICE,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_INNER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
};

MMU_Long_Format_Config mmu_cfg=
{
	.memory_map        = memory_ranges,
	.uiNumMemMapRanges = sizeof(memory_ranges)/sizeof(MMU_Memory_Map_Range),
	.ullpMMU3rdLevelTT = NULL, 	//No level 3 translation table
	.bAlignCheck       = TRUE,
	.tableCacheAttr    = MMU_TAB_ATTR_CACHE_WB_WA,
	.tableShareAttr	   = MMU_MEM_ATTR_INNER_SHARE,
};

extern void PCIE_CPU_test(PCIERemoteTestAddress* remoteAddr);
extern void PCIE_edma_test(PCIERemoteTestAddress* remoteAddr);

/*simple memory test to verfiy basic function of PCIE */
unsigned int PCIE_Mem_Test(unsigned int uiStartAddress,
	unsigned int uiTotalByteCount, unsigned int uiFillByteCount)
{
	unsigned int uiFailCount;
	
	uiFailCount = Memory_quick_test(uiStartAddress, uiTotalByteCount, 
		uiFillByteCount, 128);

	if(0==uiFailCount)
		printf("PCIE memory test passed at address 0x%x\n",
			uiStartAddress);
			
    return uiFailCount; 
}

void PCIE_integrity_Test(PCIERemoteTestAddress * remoteAddr)
{
	/*DDR*/
	PCIE_Mem_Test(remoteAddr->DDRA_DST_ADDR, 0x800000, 0x10000);
	if(remoteAddr->DDRB_DST_ADDR)
		PCIE_Mem_Test(remoteAddr->DDRB_DST_ADDR, 0x800000, 0x10000);

	/*MSMC_RAM*/
	PCIE_Mem_Test(remoteAddr->MSMC_RAM_DST_ADDR, 0x040000, 0x10000);

	/*Core 1 LL2*/
	PCIE_Mem_Test(remoteAddr->LL2_DST_ADDR, 0x020000, 0x10000);
}

/*this interrupt test is done in loopback mode,
a MSI is trigger manually, a interrupt packet is generated and 
loopback to this Device and trigger interrupt to the CPU. The latency 
between trigger and the entry of the ISR are measured*/
void PCIE_Interrupt_Latency_Test(Uint32 uiPCIE_instant_num)
{
	Uint32 uiStartCCNT= CP15_read_CCNT();

#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs;
#else //for K2L and K2E
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs[uiPCIE_instant_num];
#endif

	PCIE_IntCCNT= 0; 	//will be updated by ISR
	
	/*manually trigger MSI, which will generate interrupt packet to remote side.
	For loopback test, the MSI_IRQ in application reister space are mapped 
	through BAR0 to first outbound window*/
	KeyStone_PCIE_generate_MSI(uiPCIE_instant_num, 8,
		(Uint32*)(CSL_PCIE_DATA_BASE+((Uint32)&PCIE_app_regs->MSI_IRQ)-(Uint32)PCIE_app_regs));

	/*the interrupt packet is loop back to this Device and trigger 
	interrupt to this CPU, here waiting for the interrupt*/
	while(0==PCIE_IntCCNT);

	/*the time stamp at the entry of the interrupt is recorded in the ISR*/
	printf("PCIE interrupt latency is %d cycles\n", PCIE_IntCCNT- uiStartCCNT);
}

void PCIE_Test(Uint32 uiPCIE_instant_num)
{
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs;
#else //for K2L and K2E
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs[uiPCIE_instant_num];
#endif

	/*for this test use CPU number as Device number,
	so, the program should be run on core 0 of Device0 and core 1 of Device1*/
	if(0==K2_Get_device_number())
	{
		/*integrity test*/
	    PCIE_integrity_Test(&PcieRemoteTestAddr); 

		/*interrupt test is done in loopback mode*/
		if(PCIE_cfg.loop_mode == PCIE_PHY_LOOPBACK)
		{
		    PCIE_Interrupt_Latency_Test(uiPCIE_instant_num);
		}

		/*------performance test------*/
		puts("#########RC starts core access performance test!#########");
		PCIE_CPU_test(&PcieRemoteTestAddr);

		puts("#########RC starts EDMA performance test!#########");
		PCIE_edma_test(&PcieRemoteTestAddr);


	}
	else
	{
		/*------test data transfer between RC and EP------
		RC writes data to EP, EP sends back the data to RC, RC verify the recievied data*/

		//handshake: send MSI to RC
		puts("#########EP starts MSI interrupt to RC!#########");
		KeyStone_PCIE_generate_MSI(uiPCIE_instant_num, 8,
			(Uint32*)(CSL_PCIE_DATA_BASE+((Uint32)&PCIE_app_regs->MSI_IRQ)-(Uint32)PCIE_app_regs));

		puts("EP standby for access by PCIE...");
		while(1);
	}
	
	//print_PCIE_status();
}

int main()
{
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs;
CSL_Pcie_cfg_space_endpointRegs	*PCIE_EP_regs = gpPCIE_EP_regs;
CSL_Pcie_cfg_space_endpointRegs	*PCIE_remote_EP_regs =gpPCIE_remote_EP_regs;
#else //for K2L and K2E
CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs[TESTED_PCIE_INSTANT_NUM];
CSL_Pcie_cfg_space_endpointRegs	*PCIE_EP_regs = gpPCIE_EP_regs[TESTED_PCIE_INSTANT_NUM];
CSL_Pcie_cfg_space_endpointRegs	*PCIE_remote_EP_regs =gpPCIE_remote_EP_regs[TESTED_PCIE_INSTANT_NUM];
#endif

	/*common initialization for internal modules in K2 device 
	enable GIC, memory protection interrupts, EDC for MSMC RAM */
	K2_common_device_init();
	/*initialize GIC interface for CPU, enable IRQ, FIQ, PMU*/
	K2_common_CPU_init();

	//enable exception handling
	KeyStone_Exception_cfg(TRUE);

	/*clear all configuration data structure, make sure unused parameters are 0*/
	memset(&prefetch_regions       , 0, sizeof(prefetch_regions       )); 
	memset(&nonfetch_regions       , 0, sizeof(nonfetch_regions       ));
	memset(&inbound_memory_regions , 0, sizeof(inbound_memory_regions ));
	memset(&outbound_memory_regions, 0, sizeof(outbound_memory_regions));
	memset(&PCIE_int_cfg           , 0, sizeof(PCIE_int_cfg          ));
	memset(&rc_cfg                 , 0, sizeof(rc_cfg                 ));
	memset(&remote_cfg_setup       , 0, sizeof(remote_cfg_setup       ));
	memset(&PCIE_cfg               , 0, sizeof(PCIE_cfg               ));

	//make PCIE access to MSMC RAM sharable with ARM core
	K2_SMS_MPAX_init(PRIVID_PCIE, MSMC_SHARE);
#if defined(DEVICE_K2L)||defined(DEVICE_K2E)
	K2_SMS_MPAX_init(PRIVID_PCIE1, MSMC_SHARE);
#endif

	//CPU speed= MAIN_PLL_REF_CLK_MHZ*MAIN_PLL_MULTIPLIER/MAIN_PLL_DIVISOR
	KeyStone_main_PLL_init(MAIN_PLL_REF_CLK_MHZ, MAIN_PLL_MULTIPLIER, MAIN_PLL_DIVISOR);

#ifndef DEVICE_K2E 	//K2E only has main PLL for both ARM and DSP cores
	//ARM core speed= ARM_PLL_REF_CLK_MHZ*ARM_PLL_MULTIPLIER/ARM_PLL_DIVISOR
	K2_ARM_PLL_init(ARM_PLL_REF_CLK_MHZ, ARM_PLL_MULTIPLIER, ARM_PLL_DIVISOR);
	//K2_ARM_PLL_init(125, 8, 1);
#endif

	//DDR configuration
	K2_DDR3A_config(NULL, NULL);
#ifdef DDRB_TEST
	K2_DDR3B_config(NULL, NULL);
#endif

	/*MMU configure for ARM core*/
	MMU_long_format_init(&mmu_cfg);
	CP15_ICacheEnable();
	CP15_DCacheEnable();

	calc_cycle_measure_overhead();

	serdes_cfg.inputRefClock = PCIE_SERDES_REF_CLK;
	serdes_cfg.linkSpeed_GHz = 5.f;
	if(PCIE_PHY_LOOPBACK==loop_mode)
		serdes_cfg.loopBackMode= CSL_SERDES_LOOPBACK_ENABLED;
	else
		serdes_cfg.loopBackMode= CSL_SERDES_LOOPBACK_DISABLED;
	serdes_cfg.numLanes= 2;

#ifdef DEVICE_K2L
	PCIE_cfg.num_lanes= 1;
#else 	//for K2H or K2K, K2E
	PCIE_cfg.num_lanes= 2;
#endif
	PCIE_cfg.bPcieGen2= TRUE;
	PCIE_cfg.loop_mode= loop_mode;

	PCIE_cfg.address_width= address_width;

	/*enable all PCIE interrupt reception*/
	PCIE_int_cfg.MSI_rx_enable_mask= 0xFFFFFFFF;
	PCIE_int_cfg.Err_rx_enable = TRUE;
	PCIE_int_cfg.PMRST_rx_enable =TRUE;
	
	/*number of MSI may generate from this EP*/
	PCIE_int_cfg.number_tx_MSI = PCIE_16_MSI;
	PCIE_cfg.interrupt_cfg= &PCIE_int_cfg;
	
	PCIE_cfg.outbound_memory_regions= &outbound_memory_regions;
	PCIE_cfg.inbound_memory_regions= &inbound_memory_regions;

	outbound_memory_regions.OB_size= PCIE_OB_SIZE_8MB;

	rc_cfg.memory_base= PCIE_NONFETCH_BASE_ADDRESS;
	rc_cfg.memory_limit= PCIE_NONFETCH_BASE_ADDRESS+256*1024*1024-1;
	rc_cfg.prefetch_memory_base= PCIE_PREFETCH_BASE_ADDRESS;
	rc_cfg.prefetch_memory_limit= PCIE_PREFETCH_BASE_ADDRESS+256*1024*1024-1;
	rc_cfg.BAR0_address= PCIE_RC_BAR0_ADDRESS;
	
	//for loopback test
	if(PCIE_PHY_LOOPBACK==loop_mode)
	{//loopback only support in RC mode
		PCIE_cfg.PcieMode= PCIE_RC_MODE;
		PCIE_cfg.rc_cfg= &rc_cfg;

		/*for loopback test all memories are mapped to one prefeachable BAR
		because RC only has one memory BAR*/
		prefetch_regions.memory_regions= memory_regions;
		prefetch_regions.uiNumRegions= 4;
		prefetch_regions.bPrefetchable= TRUE;
		inbound_memory_regions.prefetch_regions= &prefetch_regions;
		printf("PCIE PHY loopback mode at %.1fGHz.\n", serdes_cfg.linkSpeed_GHz);
	}
	else //for 2 Device test
	{
		if(0==K2_Get_device_number())
		{//first Device is the RC
			PCIE_cfg.PcieMode= PCIE_RC_MODE;
			PCIE_cfg.rc_cfg= &rc_cfg;

			/*for this test, two devices are connect directly,
			the bus, device and function number are all 0*/
			remote_cfg_setup.config_type= 0; 	//remote device is EP
			remote_cfg_setup.config_bus= 0;
			remote_cfg_setup.config_device= 0;
			remote_cfg_setup.config_function= 0;
			printf("PCIE normal and RC mode at %.1fGHz, should be running on core0.\n", serdes_cfg.linkSpeed_GHz);
		}
		else
		{//the second Device is the EP
			PCIE_cfg.PcieMode= PCIE_EP_MODE;

			/*for 2 Device test, at EP side, DDR are mapped to prefetchable BAR;
			MSMC_RAM and LL2 are mapped to nonprefetchable BAR*/
			prefetch_regions.memory_regions= &memory_regions[0];
			prefetch_regions.uiNumRegions= 2;
			prefetch_regions.bPrefetchable= TRUE;
			nonfetch_regions.memory_regions= &memory_regions[2];
			nonfetch_regions.uiNumRegions= 2;
			nonfetch_regions.bPrefetchable= FALSE;
			inbound_memory_regions.prefetch_regions= &prefetch_regions;
			inbound_memory_regions.nonfetch_regions= &nonfetch_regions;

			/*in EP mode, outbound memory regions must be setup manually.
			in RC mode, outbound memory regions is setup via enumeration*/
			outbound_memory_regions.address_offset= EP_OB_PCIE_address;
			outbound_memory_regions.uiNumRegions= sizeof(EP_OB_PCIE_address)/8; 
			printf("PCIE normal and EP mode at %.1fGHz, should be running on core1.\n", serdes_cfg.linkSpeed_GHz);
		}
	}

	//PCIE initialize
	K2_PCIE_enable(TESTED_PCIE_INSTANT_NUM, &PCIE_cfg);

#if defined(DEVICE_K2L)
	K2_PCIE_Serdes_init(&serdes_cfg, CSL_CSISC2_3_SERDES_CFG_REGS);
#elif defined(DEVICE_K2E)
	K2_PCIE_Serdes_init(&serdes_cfg, CSL_PCIE_0_SERDES_CFG_REGS +
		TESTED_PCIE_INSTANT_NUM*(CSL_PCIE_1_SERDES_CFG_REGS-CSL_PCIE_0_SERDES_CFG_REGS));
#else 	//K2K or K2H
	K2_PCIE_Serdes_init(&serdes_cfg, CSL_PCIE_SERDES_CFG_REGS);
#endif

	K2_PCIE_Init(TESTED_PCIE_INSTANT_NUM, &PCIE_cfg);

	/*PCIE MSI allocation for one device.*/
	if(PCIE_PHY_LOOPBACK==loop_mode)
	{
		/*for loopback test, the MSI CAP registers are accessed through local bus. 
		The MSI_IRQ in application reister space are mapped through BAR0 to 
		PCIE_RC_BAR0_ADDRESS*/
		KeyStone_PCIE_RC_MSI_allocate((PCIE_MSI_Regs *)&PCIE_EP_regs->MSI_CAP,
			PCIE_RC_BAR0_ADDRESS+((Uint32)&PCIE_app_regs->MSI_IRQ)-(Uint32)PCIE_app_regs);
	}
	else
	{
		if(0==K2_Get_device_number())
		{//first Device is the RC

			/*Remote Configuration Transaction Setup,
			select the bus, device and function number of the target*/
			KeyStone_PCIE_remote_CFG_setup(TESTED_PCIE_INSTANT_NUM, &remote_cfg_setup);

			/*for test between two device, the RC should access MSI CAP register of EP throught its remote
			configuration space, MSI PCIE write address depends on RC's PCIE address*/
			KeyStone_PCIE_RC_MSI_allocate((PCIE_MSI_Regs *)&PCIE_remote_EP_regs->MSI_CAP,
				PCIE_RC_BAR0_ADDRESS+((Uint32)&PCIE_app_regs->MSI_IRQ)-(Uint32)PCIE_app_regs);
		}
	}

	/*PCIE address allocation. setup outbound and inbound address mapping*/
	KeyStone_PCIE_Address_setup(TESTED_PCIE_INSTANT_NUM, &PCIE_cfg);

	//clear all interrupt status registers of PCIE
	KeyStone_PCIE_clear_interrupts(TESTED_PCIE_INSTANT_NUM);
	//interrupt route initialization
	PCIE_Interrupts_Init();

	PCIE_Test(TESTED_PCIE_INSTANT_NUM);

	puts("PCIE test complete.");
	return 0;
}

