LitePCIe Documentation

repository·master·Indexed 20 days ago

https://github.com/enjoy-digital/litepcie

A small-footprint, highly configurable PCIe core implemented using Migen and LiteX for FPGA-based SoCs. It supports Xilinx Ultrascale(+), Xilinx 7-Series, and Intel Cyclone5, providing hardware RTL and a Linux driver. Key features include Scatter-Gather DMA, MMAP with AXI and Wishbone interfaces, and support for MSI/MSI-X. The accompanying Linux driver exposes DMA channels as character devices (/dev/litepcieN) supporting read, write, mmap, and various ioctl commands for hardware control.

Tokens
1.9K
Snippets
6
Records
10
Agent score
22%

What's inside LitePCIe

  1. Overview of LitePCIe features and architecture

    master

    LitePCIe is a highly configurable PCIe core powered by Migen. It is structured into three main layers:

    PHY Layer

    Supports various FPGA families and datapath widths:

    • Xilinx Ultrascale(+): up to PCIe Gen3 X16
    • Xilinx 7-Series: up to PCIe Gen2 X8
    • Intel Cyclone5: up to PCIe Gen2 X4
    • Datapath widths: 64, 128, 256, or 512-bit
    • Includes Clock domain crossing support.

    Core Layer

    Handles the protocol logic:

    • TLP (Transaction Layer Packet) layer
    • Reordering logic
    • MSI (Single, Multi-vector) and MSI-X support
    • Crossbar implementation

    Frontend Layer

    Provides interfaces for system integration and software:

    • DMA: Supports Scatter-Gather.
    • MMAP: Provides AXI, Wishbone Slave, and Wishbone Master interfaces.
    • PTM: Supported on Xilinx 7-Series/Gen2 X1.
    • Software: Includes a Linux Driver supporting both MMAP and DMA.
  2. Getting started with LitePCIe

    master

    To use LitePCIe, you must set up a Python environment and the necessary FPGA vendor tools. LitePCIe is designed to be used as a LiteX library or integrated into standard design flows by generating Verilog RTL.

    1. Install Python 3.8+.
    2. Install your FPGA vendor's development tools.
    3. Install LiteX and the required cores by following the official LiteX installation guide.

    For integration examples, refer to the examples directory in this repository or check LiteX-Boards for LiteX-specific integration patterns.

  3. Run LitePCIe unit tests

    master

    LitePCIe includes unit tests located in the ./test/ directory. You can run the full test suite using the provided setup script or run specific tests using the Python unittest module.

    # Run all unit tests
    $ ./setup.py test
    
    # Run a specific test
    $ python3 -m unittest test.test_name
  4. Map LitePCIe DMA buffers via mmap

    master

    To achieve zero-copy data transfers, you can mmap the DMA buffers of a LitePCIe channel. The memory layout is determined by DMA_BUFFER_TOTAL_SIZE and DMA_BUFFER_COUNT.

    Memory Layout:

    • TX Buffers (Host to FPGA): Mapped at offset 0. These correspond to the reader_addr buffers in the kernel.
    • RX Buffers (FPGA to Host): Mapped at offset DMA_BUFFER_TOTAL_SIZE. These correspond to the writer_addr buffers in the kernel.

    Important Note on Cache Coherency: On certain architectures (like ARM/AArch64), the memory is mapped as cached. Users must explicitly flush CPU caches when performing transfers to ensure data consistency between the CPU and the FPGA.

    /* Conceptual mmap usage */
    int fd = open("/dev/litepcie0", O_RDWR);
    size_t total_size = DMA_BUFFER_TOTAL_SIZE;
    
    // Map RX buffers (FPGA -> Host)
    void *rx_ptr = mmap(NULL, total_size, PROT_READ, MAP_SHARED, fd, total_size);
    
    // Map TX buffers (Host -> FPGA)
    void *tx_ptr = mmap(NULL, total_size, PROT_WRITE, MAP_SHARED, fd, 0);
  5. LitePCIe Kernel Module Lifecycle

    master

    The LitePCIe kernel driver manages the interaction between the PCI hardware and the Linux character device subsystem.

    When the module is loaded (litepcie_module_init), it performs the following sequence:

    1. Creates a device class: Uses class_create with the name LITEPCIE_NAME to allow user-space tools to discover LitePCIe devices.
    2. Allocates character device regions: Uses alloc_chrdev_region to reserve a range of character device numbers (major/minor) for LitePCIe.
    3. Registers the PCI driver: Uses pci_register_driver with the litepcie_pci_driver structure to bind the driver to matching LitePCIe hardware identified in the litepcie_pci_ids table.

    When the module is unloaded (litepcie_module_exit), it reverses these steps by unregistering the PCI driver, unregistering the character device region, and destroying the device class.

  6. Use LitePCIe character devices for DMA

    master

    The LitePCIe kernel driver exposes DMA channels as character devices in /dev/litepcieN, where N is the channel index. Users can interact with these channels using standard file operations:

    • read(): Reads data from the DMA writer (the data sent from the FPGA to the host). It blocks until data is available or returns -EAGAIN if O_NONBLOCK is set.
    • write(): Writes data to the DMA reader (the data sent from the host to the FPGA). It blocks until there is space in the DMA buffers or returns -EAGAIN if O_NONBLOCK is set.
    • mmap(): Maps the DMA buffers directly into the user's address space for high-performance zero-copy access. The mapping is split into two parts:
      • Offset 0: The TX (Transmit) buffers (DMA Reader buffers).
      • Offset DMA_BUFFER_TOTAL_SIZE: The RX (Receive) buffers (DMA Writer buffers).
    • poll(): Allows monitoring the device for readiness (data available to read or space available to write).
    • ioctl(): Used for advanced control, such as enabling/disabling DMA, setting loopback mode, or accessing hardware registers.
    /* Example conceptual usage of the character device */
    int fd = open("/dev/litepcie0", O_RDWR);
    
    // Read data from FPGA
    char buffer[DMA_BUFFER_SIZE];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
    
    // Write data to FPGA
    ssize_t bytes_written = write(fd, buffer, sizeof(buffer));
    
    // Use poll to wait for data
    struct pollfd pfd;
    pfd.fd = fd;
    pfd.events = POLLIN;
    poll(&pfd, 1, -1);
    
    close(fd);
  7. Control LitePCIe via IOCTL commands

    master

    The LitePCIe driver provides several ioctl commands to control hardware features and DMA state. Common commands include:

    • LITEPCIE_IOCTL_REG: Read or write a specific 32-bit register address on the LitePCIe device. Requires a struct litepcie_ioctl_reg argument.
    • LITEPCIE_IOCTL_DMA: Enables or disables DMA loopback mode. Requires a struct litepcie_ioctl_dma argument.
    • LITEPCIE_IOCTL_DMA_WRITER: Enables/disables the DMA writer and retrieves current hardware/software buffer counts. Requires struct litepcie_ioctl_dma_writer.
    • LITEPCIE_IOCTL_DMA_READER: Enables/disables the DMA reader and retrieves current hardware/software buffer counts. Requires struct litepcie_ioctl_dma_reader.
    • LITEPCIE_IOCTL_MMAP_DMA_INFO: Retrieves information about DMA buffer offsets and sizes for correct mmap usage. Requires struct litepcie_ioctl_mmap_dma_info.
    • LITEPCIE_IOCTL_MMAP_DMA_WRITER_UPDATE / LITEPCIE_IOCTL_MMAP_DMA_READER_UPDATE: Updates the software-side buffer counts. Requires struct litepcie_ioctl_mmap_dma_update.
    • LITEPCIE_IOCTL_LOCK: Manages exclusive access to reader/writer channels. Requires struct litepcie_ioctl_lock.

    Note: Some commands like LITEPCIE_IOCTL_FLASH (SPI) and LITEPCIE_IOCTL_ICAP are conditionally available based on hardware configuration.