FPGA NVME SSD

一,环境搭建

1,安装Petalinux插件:将所需的依赖库都先安装(参考UG1144文档要求)

sudo apt-get install tofrodos iproute2 gawk

sudo apt-get install gcc git make

sudo apt-get install xvfb

sudo apt-get install net-tools libncurses5-dev tftpd

sudo apt-get install zlib1g-dev zlib1g-dev:i386 libssl-dev flex bison libselinux1

sudo apt-get install gnupg wget diffstat chrpath socat xterm

sudo apt-get install autoconf libtool tar unzip texinfo zlib1g-dev gcc-multilib build-essential libsdl1.2-dev libglib2.0-dev

sudo apt-get install screen pax gzip tar

sudo apt-get install openssl

2,Ubuntu系统默认的/bin/sh是dash,而PetaLinux要求的是bash在终端中,执行:sudo dpkg-reconfigure bash

3,进入.run文件的目录,修改其执行属性:chmod +x petalinux-v2017.4-final-installer.run

4,安装PetaLinux

因为安装PetaLinux时不能用root权限,所以先在非root目录下创建一个文件夹,并且修改权限为:755

mkdir -p /home/alinx/linux-xlnx-xilinx-v2017.4

chmod 755 /home/alinx/linux-xlnx-xilinx-v2017.4

在/home目录下创建petalinux文件夹,并且修改权限为755.

进入.run文件所在的目录,执行安装命令。

./petalinux-v2017.4-final-installer.run /home/alinx/linux-xlnx-xilinx-v2017.4

等待一段时间.run安装包的校验、提取,然后出现许可文件的阅读,按Enter回车键阅读,按Q键退出,按Y键接受许可,需要操作3次,然后等待PetaLinux安装结束。

INFO: Checking installer checksum...

INFO: Extracting PetaLinux installer...

LICENSE AGREEMENTS

PetaLinux SDK contains software from a number of sources.  Please review

the following licenses and indicate your acceptance of each to continue.

You do not have to accept the licenses, however if you do not then you may

not use PetaLinux SDK.

Use PgUp/PgDn to navigate the license viewer, and press 'q' to close

Press Enter to display the license agreements

Do you accept Xilinx End User License Agreement? [y/N] > y

Do you accept Webtalk Terms and Conditions? [y/N] > y

Do you accept Third Party End User License Agreement? [y/N] > y

5,验证PetaLinux安装成功

(1)先设置环境变量

source /opt/Xilinx/Vivado/2017.4/settings64.sh  

source /opt/pkg/petalinux/settings.sh

(2)验证环境变量设置成功

echo $PETALINUX

如果出现安装路径,则表示成功。

/home/alinx/linux-xlnx-xilinx-v2017.4

二,vivado搭建过程:

PCIE采用了全双工的传输设计,即允许在同一时刻,同时进行发送和接收数据。如下图所示,设备A和设备B之间通过双向的Link相连接,每个Link支持1到32个通道(Lane)。由于是串行总线,因此所有的数据(包括配置信息等)都是以数据包为单位进行发送的。

vivado中有三种方式实现PCIE数据传输:Programmed I/O(PIO),Peer-to-Peer和DMA。

调用7 Series Integrated Block for PCI Express IP核,这是最基础的PCIE IP核,比较复杂。
FPGA NVME SSD_第1张图片

 PCIE IP核所做的工作主要有两点,一个是将TLP包转换成AXI协议;另一个是支持DMA操作。

FPGA NVME SSD_第2张图片

 FPGA NVME SSD_第3张图片

 

调用AXI Memory Mapped To PCI Express IP核,对7 Series Integrated Block for PCI Express进一步封装,使用Example Design直接运行,但需要添加DMA IP核实现DMA数据传输。

 1,PCIE basics
FPGA NVME SSD_第4张图片

 2,PCIE link config

FPGA NVME SSD_第5张图片

 3,PCIE ID

FPGA NVME SSD_第6张图片

  4,PCIE BARS

5,PCIE misc

FPGA NVME SSD_第7张图片

  6,AXI BARS

FPGA NVME SSD_第8张图片

  7,AXI system

FPGA NVME SSD_第9张图片

8,将vivado PCIE_NVME工程FILE->EXPORT->EXPORT HARDWARE,导入生成PCIE_NVME.hdf文件,最终会复制到复制到 zynq_pcie_nvme中的hdf_location.sdk文件夹里面

FPGA NVME SSD_第10张图片

顶层文件:

`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

10、约束文件:

#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]}]

PCIE 发送和接收:

FPGA NVME SSD_第11张图片

 FPGA NVME SSD_第12张图片

 FPGA NVME SSD_第13张图片

TLP FPGA 接收端:

FPGA NVME SSD_第14张图片

 FPGA NVME SSD_第15张图片

FPGA NVME SSD_第16张图片

 

 

四,在SDK裸机阶段可以先自行测试,就像axi_ethernet PL扩展网口类似。

1,新建fsbl文件,测试文件添加pcie_gen2_enumerate.c,platform_config.h,platform.c,platform.h

FPGA NVME SSD_第17张图片

 2,下载验证:接上PS串口根据这些打印信息,判断PCIE有没有初始化或测试成功

FPGA NVME SSD_第18张图片

FPGA NVME SSD_第19张图片

FPGA NVME SSD_第20张图片

SDK代码:main.c/pcie_gen2_enumerate.c

#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
/*
 * Command register offsets
 */
#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
#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
*****************************************************************************/
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
******************************************************************************/
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)
******************************************************************************/
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,...
******************************************************************************/
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 MB caches are disabled or do not exist, this delay loop could
     * take minutes instead of seconds (e.g., 30x longer).  Print a warning
     * message for the user (once).  If only MB had a built-in timer!
     */
    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
}

 

三,带系统的编译:

1,导入硬件:将*.hdf复制到虚拟机里面

 FPGA NVME SSD_第21张图片

 FPGA NVME SSD_第22张图片

 FPGA NVME SSD_第23张图片

 2,导入hdf硬件:petalinux-config --get-hw-description ../linux_base.sdk 

FPGA NVME SSD_第24张图片

 FPGA NVME SSD_第25张图片

 FPGA NVME SSD_第26张图片

 FPGA NVME SSD_第27张图片

 FPGA NVME SSD_第28张图片

3,启动方式配置

4,命令配置Linux内核: petalinux-config -c kernel

    我们使用内核配置菜单来启用PCI支持并为NVMExpress设备启用驱动程序:

(1)启用:总线选项-> PCI支持

         启用:总线选项-> PCI支持->消息信号中断(MSI和MSI-X)

         启用:总线选项-> PCI支持->启用PCI资源重新分配检测 
FPGA NVME SSD_第29张图片

 FPGA NVME SSD_第30张图片

 (2)启用:总线选项-> PCI支持-> PCI主机控制器驱动程序-> Xilinx AXI PCIe主机桥支持

FPGA NVME SSD_第31张图片

 

 3)启用:设备驱动程序->块设备-> NVM Express块设备

 FPGA NVME SSD_第32张图片

 FPGA NVME SSD_第33张图片

 5,要配置Linux根文件系统,请运行以下命令: petalinux-config -c rootfs
   (1)配置根文件系统以包括一些实用程序,我们将需要设置NVMe PCIe SSD:
· 启用PCI utils(用于lspci):文件系统包->控制台/ utils-> pciutils-> pciutils
FPGA NVME SSD_第34张图片

 FPGA NVME SSD_第35张图片

 FPGA NVME SSD_第36张图片

 FPGA NVME SSD_第37张图片

 FPGA NVME SSD_第38张图片

 

(2)为lsblk,fdisk,mkfs,blkid启用必需的软件包:
· 文件系统软件包-> base-> util-linux-> util-linux
· 文件系统软件包->base-> util-linux-> util-linux-blkid
· 文件系统软件包-> base-> util-linux-> util-linux-fdisk

· 文件系统软件包->base-> util-linux-> util-linux-mkfs
· 文件系统软件包->base-> util-linux-> util-linux-mount
 

 FPGA NVME SSD_第39张图片

 FPGA NVME SSD_第40张图片

 FPGA NVME SSD_第41张图片

 

(3)配置根文件 e2fsprogs

  文件系统软件包->base-> e2fsprogs-> e2fsprogs
· 文件系统软件包->base-> e2fsprogs-> e2fsprogs-mke2fs

 FPGA NVME SSD_第42张图片

 FPGA NVME SSD_第43张图片

 

6,使用命令构建PetaLinux: petalinux-build

   (方法1)生成启动文件:petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga --u-boot --force 

   (方法2)我们先加载比特流,然后加载内核:

  · petalinux-boot --jtag --fpga --bitstream ../impl_1/design_1_wrapper.bit

·   petalinux-boot --jtag --kernel

  通过JTAG传输内核将需要几分钟。等待命令行返回,然后可能还要花10到20秒钟,可以在Putty终端上看到系统启动打印
FPGA NVME SSD_第44张图片

 

五,系统启动验证

 1,在PetaLinux中设置NVME SSD

  使用命令检查SSD是否已被识别为块设备:lsblk

FPGA NVME SSD_第45张图片

 FPGA NVME SSD_第46张图片

 使用命令在新分区上创建文件系统mkfs -t ext2 /dev/nvme0n1p1

FPGA NVME SSD_第47张图片

 FPGA NVME SSD_第48张图片

 附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
}

 

 

 

你可能感兴趣的:(SSD,FPGA/ASCI,Linux,驱动,ubuntu,linux,运维)