Device driver

coyote_driver.h

Top-level file of the Coyote driver; entry and exit point.

Functions

static int __init coyote_init (void)

Top-level function of the Coyote driver, called when the driver is inserted. This function simply calls the pci_init() function, which is responsible for setting up the FPGA, vFPGAs, memory mappings etc. (see the documentation)

NOTE: In the past, we used to support Enzian (ECI) but it has been deprecated as of 2024. If you would like to add support for Enzian, reach out to us on GitHub or check how the code use to look before, with the diff commit being: 4555431cf251100e2f16255f7f49e9f02ddfb96d

static void __exit coyote_exit (void)

Reverse of the init function, called when the driver is removed Handles device clean-up, memory freeing etc. See the documentation in pci_dev

coyote_setup.h

Functions for initializing, setting up and freeing Coyote devices (vfpga_dev, reconfig_dev) and utility functions (set up sysfs, reading config etc.)

Functions

int read_shell_config(struct bus_driver_data *data)

Reads the synthesized shell configuration and populates fields of bus_driver_data The configuration was specifified during the hardware synthesis in CMakeLists.txt The method also prints the configuration to the kernel log; can be queryed with dmesg

int allocate_card_resources(struct bus_driver_data *data)

Allocates and initializes metadata structs used for managing card memory, if enabled.

void free_card_resources(struct bus_driver_data *data)

Releases metadata structs used for managing card memory, if enabled; opposite of allocate_card_resources.

void init_spin_locks(struct bus_driver_data *data)

Initialize general (not used by individual vFPGAs) spin locks used in Coyote; used to protect shared resources and ensure safe access.

int create_sysfs_entry(struct bus_driver_data *data)

Initialize sysfs entry for Coyote; for more details see coyote_sysfs.h.

void remove_sysfs_entry(struct bus_driver_data *data)

Removes sysfs entry for Coyote; used oly when the driver is unloaded.

int alloc_vfpga_devices(struct bus_driver_data *data, dev_t device)

Allocates and registers all the char vFPGA devices (one for every region)

int setup_vfpga_devices(struct bus_driver_data *data)

Sets up the previously allocated vFPGA char devices (above); memory mapping registers, initializing work queues, mutexes etc.

void teardown_vfpga_devices(struct bus_driver_data *data)

Releases resources used by vFPGA char devices; destroys work queues etc., opposite of setup_vfpga_devices.

void free_vfpga_devices(struct bus_driver_data *data)

Frees the allocated vFPGA char devices and unregisters it from the OS; opposite of alloc_vfpga_devices.

int alloc_reconfig_device(struct bus_driver_data *data, dev_t device)

Allocates a char reconfig_device which is used to interact with the static layer for shell reconfiguration.

int setup_reconfig_device(struct bus_driver_data *data)

Sets up the previously allocated reconfig char device (above); initializing work queues, mutexes, hash tables etc.

void teardown_reconfig_device(struct bus_driver_data *data)

Releases resources used by the reconfig device; destroys work queues etc., opposite of setup_reconfig_device.

void free_reconfig_device(struct bus_driver_data *data)

Frees the allocated reconfig char device and unregisters it from the OS; opposite of alloc_reconfig_device.

coyote_sysfs.h

Coyote sysfs module.

sysfs is a virtual filesystem in Linux that exposes kernel objects and their attributes to the user-space It can be used for reading and writing various attributes of devices in the kernel-space The following methods retrieve or set attributes from memory-mapped FPGA registers The attributes can also be read and set from a standard Linux terminal, e.g., cat /sys/kernel/coyote_sysfs_0/<attribute>

In general, the methods in this file follow similar steps:

  1. Parse a generic kernel object (kobj) into a Coyote-specific variable of type bus_driver_data

  2. Ensure the parsed object is non-null, using BUG_ON(…)

  3. Retrieve or set the target attribute

Functions

ssize_t cyt_attr_ip_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get FPGA IP address.

ssize_t cyt_attr_ip_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)

Set FPGA IP address.

ssize_t cyt_attr_mac_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get FPGA MAC address.

ssize_t cyt_attr_mac_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)

Set FPGA MAC address.

ssize_t cyt_attr_eost_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get PR end of start-up (EOS) time.

ssize_t cyt_attr_eost_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)

Set PR end of start-up (EOS) time.

ssize_t cyt_attr_nstats_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get network stats on port QSFP0.

ssize_t cyt_attr_xstats_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get XDMA stats.

ssize_t cyt_attr_prstats_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get partial reconfiguration stats.

ssize_t cyt_attr_engines_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get engine stats.

ssize_t cyt_attr_cnfg_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

Get Coyote FPGA configuration (N_REGIONS, EN_MEM, EN_STRM, EN_PR, EN_RDMA, TLB config etc.)

vfpga_gup.h

Header file for memory management of vFPGAs.

This file defines the functions for handling page faults, managing user pages, exporting necessary DMA Buffers for FPGA-GPU communication. NOTE: GUP stands for “Get User Pages”, which is a Linux kernel mechanism to pin user-space pages in memory and Coyote swaps the pages between host and card. NOTE: Previously (currently in LEGACY), there was an alternative memory management mechanism, using Linux’s Hetereogeneous Memory Management (HMM) framework.

The mapped user pages are stored in a hash_table, called user_buff_map Functions in this file implement high-level logic for managing Coyote memory And call functions in vfpga_hw.h that handle the low-level logic by writing to the memory-mapped registers in the FPGA Additionally, this file provides functions for peer-to-peer DMA Buffer management, enabling direct FPGA-GPU communication.

Important functions in this file include:

  • mmu_handler_gup; the top-level function, handles page faults issued by the FPGA

  • tlb_map_gup and tlb_unmap_gup; which maps/unmaps user pages to the TLB

  • offload_user_pages and sync_user_pages; handling the offloading and syncing of user pages between host and card

  • P2P DMA Buffer functions for managing DMA Buffers to FPGA-GPU communication

Functions

int mmu_handler_gup(struct vfpga_dev *device, uint64_t vaddr, uint64_t len, int32_t ctid, int32_t stream, pid_t hpid)

Top-level function; handles page faults issued by the FPGA.

Parameters:
  • device – vFPGA char device

  • vaddr – Buffer virtual address corresponding to the page fault

  • len – Length, in bytes, of the page-faulting buffer

  • ctid – Coyote thread ID

  • stream – Access type: HOST (1) or CARD (0)

  • hpid – Host process ID

Returns:

0 on success, negative error code on failure

struct user_pages *map_present(struct vfpga_dev *device, struct pf_aligned_desc *pf_desc)

Checks if a mapping is already present in the user buffer map.

Parameters:
  • device – vFPGA char device

  • pf_desc – Aligned page fault descriptor; holds info about virtual address, length etc.

Returns:

Pointer to the user_pages structure if mapping exists, NULL otherwise

void tlb_map_gup(struct vfpga_dev *device, struct pf_aligned_desc *pf_desc, struct user_pages *user_pg, pid_t hpid)

Creates a TLB mapping for the given user pages.

Parameters:
  • device – vFPGA char device

  • pf_desc – Aligned page fault descriptor; holds info about virtual address, length etc.

  • user_pg – User pages structure

  • hpid – Host process ID

void tlb_unmap_gup(struct vfpga_dev *device, struct user_pages *user_pg, pid_t hpid)

Removes a TLB mapping for the given user pages.

Parameters:
  • device – vFPGA char device

  • pf_desc – Aligned page fault descriptor; holds info about virtual address, length etc.

  • hpid – Host process ID

struct user_pages *tlb_get_user_pages(struct vfpga_dev *device, struct pf_aligned_desc *pf_desc, pid_t hpid, struct task_struct *curr_task, struct mm_struct *curr_mm)

Pins user pages and prepares them for TLB mapping.

A function that is first called when no mapping for a user page exists In this case, the function performs the following:

  • Allocates a new user_pages struct which holds informaton about page physical address, length etc.,

  • Pins the pages, to avoid them being swapped out

  • Flushes the cache to ensure data consistency between the FPGA and the CPU

  • Allocates the corresponding card buffer, if memory is enabled This function is immediately followed by a call to tlb_map_gup, which maps the user pages to the vFPGA’s TLB

Parameters:
  • device – vFPGA char device

  • pf_desc – Aligned page fault descriptor

  • hpid – Host process ID

  • curr_task – Current task structure

  • curr_mm – Current memory management structure

Returns:

Pointer to the user_pages structure on success, NULL on failure

int tlb_put_user_pages(struct vfpga_dev *device, uint64_t vaddr, int32_t ctid, pid_t hpid, int dirtied)

Releases user pages and removes their TLB mappings.

Parameters:
  • device – vFPGA char device

  • vaddr – Starting virtual address

  • ctid – Coyote thread ID for which the pages were mapped

  • hpid – Host process ID for which the pages were mapped

  • dirtied – Indicates if the pages were modified

Returns:

0 on success, negative error code on failure

int tlb_put_user_pages_ctid(struct vfpga_dev *device, int32_t ctid, pid_t hpid, int dirtied)

Releases all user pages for a given Coyote thread.

Parameters:
  • device – vFPGA char device

  • ctid – Coyote thread ID for which the pages should be released

  • hpid – Host process ID associated with the Coyote thread

  • dirtied – Indicates if the pages were modified

Returns:

0 on success, negative error code on failure

void migrate_to_card(struct vfpga_dev *device, struct user_pages *user_pg)

Helper function, migrates user pages to card memory.

Parameters:
  • device – vFPGA char device

  • user_pg – User pages to be migrated

void migrate_to_host(struct vfpga_dev *device, struct user_pages *user_pg)

Helper function, migrates user pages back to the host memory.

Parameters:
  • device – vFPGA char device

  • user_pg – User pages to be migrated

int offload_user_pages(struct vfpga_dev *device, uint64_t vaddr, uint32_t len, int32_t ctid)

Trigger off-load operation; moving pages from host to card & updating mappings.

Parameters:
  • device – vFPGA char device

  • vaddr – Starting virtual address of the buffer to be offloaded

  • len – Length, in bytes, of the buffer to be offloaded

  • ctid – Coyote thread ID

Returns:

0 on success, negative error code on failure

int sync_user_pages(struct vfpga_dev *device, uint64_t vaddr, uint32_t len, int32_t ctid)

Trigger sync operation; moving pages from card to host & updating mappings.

Parameters:
  • device – vFPGA char device

  • vaddr – Starting virtual address of the buffer to be synced

  • len – Length, in bytes, of the buffer to be synced

  • ctid – Coyote thread ID

Returns:

0 on success, negative error code on failure

void p2p_move_notify(struct dma_buf_attachment *attach)

Callback for handling page movement notifications in peer-to-peer DMA.

To manage page movements in GPU memory, this routines deletes TLB entries and retrieves new entries It is passed as a parameter to the dma_buf_attach_ops struct, which is used when attaching the DMA Buffer

Parameters:

attach – DMA buffer attachment structure

int p2p_attach_dma_buf(struct vfpga_dev *device, int buf_fd, uint64_t vaddr, int32_t ctid)

Attaches a DMA buffer to the vFPGA.

In general, this functions implements similary logic as tlb_get_user_pages, but for DMA Buffers Functionality includes:

  • Allocating a new user_pages struct which holds informaton about page physical address, length etc.,

  • Attaches the DMA Buffer to the vFPGA char device, allowing the vFPGA to access the buffer

  • Maps the DMA Buffer to the FPGA’s address spae, by providing a scatter-gather list represnting the physical memory of the buffer

  • Allocate corresponding card buffer, if memory is enabled

  • Maps the physical to virtual translations to the vFPGA’s TLB using tlb_map_gup

Parameters:
  • device – vFPGA char device

  • buf_fd – File descriptor of the DMA buffer

  • vaddr – Virtual address to map the buffer

  • ctid – Coyote thread ID

Returns:

0 on success, negative error code on failure

int p2p_detach_dma_buf(struct vfpga_dev *device, uint64_t vaddr, int32_t ctid, int dirtied)

Detaches a DMA buffer from the vFPGA device.

In general, this functions implements similary logic as tlb_put_user_pages, but for DMA Buffers It is called at the end of the Coyote thread’s execution, when the buffer is no longer needed Functionality includes (which is largely opposite to p2p_attach_dma_buf):

  • Removing the TLB entries using tlb_unmap_gup

  • Freeing card memory, if memory is enabled

  • Unmapping the DMA Buffer from the vFPGA’s address space

  • Detaching the vFPGA device from the DMA Buffer

Parameters:
  • device – vFPGA char device

  • vaddr – Virtual address of the buffer

  • ctid – Coyote thread ID

  • dirtied – Indicates if the buffer was modified

Returns:

0 on success, negative error code on failure

vfpga_hw.h

vFPGA hardware functions

Functions in this file are primarily used for low-level hardware operations in the vFPGA Typically, this includes writing to or reading from hardware registers, which are memory-mapped during driver loading The registers typically start an operation in hardware or contain some control flow data (virtual address, length etc.)

Functions

uint32_t read_irq_type(struct vfpga_dev *device)

Parse interrupt type.

Parameters:

device – vFPGA char device

Returns:

type of interrupt (IRQ_DMA_OFFL, IRQ_DMA_SYNC, IRQ_INVLDT, IRQ_PFAULT, IRQ_NOTIFY)

void read_irq_notify(struct vfpga_dev *device, struct vfpga_irq_notify *irq_not)

Parse user notification IRQ.

Parameters:
  • device – vFPGA char device

  • irq_not – notification struct, to be set (updated) by this function

void read_irq_pfault(struct vfpga_dev *device, struct vfpga_irq_pfault *irq_pf)

Parse page fault IRQ.

Parameters:
  • device – vFPGA char device

  • irq_pf – page fault struct, to be set (updated) by this function

void drop_irq_pfault(struct vfpga_dev *device, bool write, int32_t ctid)

Drops the page fault, because it went wrong somewhere.

Parameters:
  • device – vFPGA char device

  • write – write operation (true) or read operation (false)

  • ctid – Coyote thread ID

void clear_irq(struct vfpga_dev *device)

Resets the IRQ registers in hardware.

Parameters:

device – vFPGA char device

void restart_mmu(struct vfpga_dev *device, bool write, int32_t ctid)

Restarts the MMU, by signaling a page fault has correctly been handled.

Parameters:
  • device – vFPGA char device

  • write – write operation (true) or read operation (false)

  • ctid – Coyote thread ID

void invalidate_tlb_entry(struct vfpga_dev *device, uint64_t vaddr, uint32_t n_pages, int32_t hpid, bool last)

Invalidate a TLB entry.

Parameters:
  • device – vFPGA char device

  • vaddr – starting virtual address of the buffer to be invalidated

  • n_pages – number of consecutive pages to be invalidated

  • hpid – host process ID

  • last – is this the last page of the buffer to be invalidated (equivalent to a tlast in an AXI stream)

void change_tlb_lock(struct vfpga_dev *device)

Locks or unlocks the TLB, potentially prevent new entries (if locked)

Parameters:

device – vFPGA char device

void create_tlb_mapping(struct vfpga_dev *device, struct tlb_metadata *tlb_meta, uint64_t vaddr, uint64_t physical_address, int32_t host, int32_t ctid, pid_t hpid)

Create a TLB mapping.

Parameters:
  • device – vFPGA char device

  • tlb_meta – helper struct, containing TLB information (page size & shift, key size & shift etc.)

  • vaddr – buffer virtual address

  • physical_address – buffer physical address

  • host – does the buffer reside in host memory (1) or in card memory (0)

  • ctid – Coyote thread ID

  • hpid – Host process ID

void create_tlb_unmapping(struct vfpga_dev *device, struct tlb_metadata *tlb_meta, uint64_t vaddr, pid_t hpid)

Unmap TLB entry.

Parameters:
  • device – vFPGA char device

  • tlb_meta – helper struct, containing TLB information (page size & shift, key size & shift etc.)

  • vaddr – buffer virtual address

  • hpid – Host process ID

void trigger_dma_offload(struct vfpga_dev *device, uint64_t *host_address, uint64_t *card_address, uint32_t n_pages, bool huge)

Triggers DMA off-load from host memory to card memory (asynchronous)

Parameters:
  • device – vFPGA char device

  • host_address – - virtual address of the buffer on the host to be off-loaded

  • card_address – - target virtual address on the card

  • n_pages – - number of pages in the buffer to be off-loaded

  • huge – - whether the buffer is using hugepages or regular pages

void trigger_dma_sync(struct vfpga_dev *device, uint64_t *host_address, uint64_t *card_address, uint32_t n_pages, bool huge)

Triggers DMA sync from card memory to host memory (asynchronous)

Parameters:
  • device – vFPGA char device

  • host_address – - target virtual address of the buffer on the host

  • card_address – - virtual address of the buffer on the card

  • n_pages – - number of pages in the buffer to be synced

  • huge – - whether the buffer is using hugepages or regular pages

int alloc_card_memory(struct vfpga_dev *device, uint64_t *card_physical_address, uint32_t n_pages, bool huge)

Allocates memory on the card’s HBM or DDR memory and updates the card_physical_address parameter with the allocated addresses.

Parameters:
  • device – vFPGA char device

  • card_physical_address – initially null/empty, set by the function to reflect the physical address of the allocated pages

  • n_pages – number of pages to be allocated

  • huge – whether the memory is using hugepages or regular pages

Returns:

whether the allocation was successful; can fail if there is insufficient space on the card

void free_card_memory(struct vfpga_dev *device, uint64_t *card_physical_address, uint32_t n_pages, bool huge)

Release memory on the card; opposite of the above alloc_card_memory function.

Parameters:
  • device – vFPGA char device

  • card_physical_address – list of pages allocated on the card’s memory and their corresponding physical addresses

  • n_pages – number of pages to be freed

  • huge – whether the memory to be freed is using hugepages or regular pages

vfpga_isr.h

vFPGA interrupts (page faults, invalidations, user interrupts etc.)

Functions

irqreturn_t vfpga_isr(int irq, void *d)

Top-level vFPGA interrupt routine; registered during set-up in msix_irq_setup(…)

Catches interrupts issued by the vFPGA (sent via XDMA and PCIe) and calls the appropriate callback method Interrupts, in the order of importance are:

  1. Completed DMA offloads/syncs

  2. Completed TLB invalidation

  3. Page fault

  4. User interrupts (notification)

For more details, refer to: Chapter 3.1.3.5 Interrupts in Abstractions for Modern Heterogeneous Systems (2024), Dario Korlija

Parameters:
  • irq – Interrupt type

  • d – Generic pointer to a Linux device; internally parsed into a vfpga_dev pointer

void vfpga_notify_handler(struct work_struct *work)

Handles user interupts (notifications)

void vfpga_pfault_handler(struct work_struct *work)

Handles vFPGA page faults, by invalidating and updating TLB; migrating data where required.

vfpga_ops.h

Standard device operations for the vfpga_dev char device: open, release, ioctl and memory map (mmap)

Functions

int vfpga_dev_open(struct inode *inode, struct file *file)

vfpga_dev open char device

int vfpga_dev_release(struct inode *inode, struct file *file)

vfpga_dev release (close) char device

long vfpga_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

vfpga_dev IOCTL calls

int vfpga_dev_mmap(struct file *file, struct vm_area_struct *vma)

vfpga_dev memory map; maps user control region, vFPGA config and writeback regions

vfpga_uisr.h

vFPGA user interrupts (notifications)

Methods to register user interrupt (notification) callbacks, as requested by the user In Coyote, user interrupts (notifications) are linked to a Coyote thread, through its ID (ctid) Therefore, there can be multiple interrupt callbacks for one vFPGA, since it’s possible to have more than one Coyote thread per vFPGA For more details, see Example 3: Multi-threading and Example 4: User interrupts

To handle interrupts, Coyote uses eventfd, a Linux kernel mechanism for event signalling eventfd can be used to communicate events between the kernel- and the user-space, or between processes In Coyote, an eventfd is created from the user-space and registered by the driver using the method vfpga_register_eventfd Then, when an interrupt from the FPGA is picked up by the driver (see vfpga_isr.c), the driver writes to the appropariate eventfd The user-space software polls on the same eventfd, and, when a change is detected, executes the appropriate callback (see sw/bThread.cpp)

Functions

int vfpga_register_eventfd(struct vfpga_dev *device, int ctid, int eventfd)

Registers an eventfd for a given Coyote thread and vFPGA device.

Parameters:
  • device – vfpga_dev for which the eventfd should be registered

  • ctid – Coyote thread ID (obtained from user-space)

  • eventfd – eventfd file descriptor, as created in the user-space (see sw/bThread.cpp)

Returns:

whether eventfd was successfully registered

void vfpga_unregister_eventfd(struct vfpga_dev *device, int ctid)

Unregisters an eventfd for a given Coyote thread and vFPGA device.

Parameters:
  • device – vfpga_dev for which the eventfd should be released

  • ctid – Coyote thread ID of the eventfd which should be released

reconfig_hw.h

Low-level hardware functionality to trigger run-time reconfiguration of the FPGA.

Functions

int reconfigure_start(struct reconfig_dev *device, uint64_t vaddr, uint64_t len, pid_t pid, uint32_t crid)

Triggers the reconfiguration process.

Parameters:
  • device – reconfig_device to be reconfigured (corresponding to the actual physical FPGA we want to reconfigure)

  • vaddr – bitstream buffer virtual address; obtained from alloc_buffer and mmap

  • pid – host process ID

  • crid – configuration ID

Returns:

reconfiguration started successfuly or not

reconfig_isr.h

Reconfiguration interrupt management; picking up interrupts when reconfiguration is complete.

Functions

irqreturn_t reconfig_isr(int irq, void *dev)

Handles incoming interrupts related to reconfiguration.

A reconfiguration interrupt issued by the FPGA corresponds to reconfiguration being completed successfuly Once picked up by this function, it sets the wait_rcnfg variable to SET, which is polled on during IOCTL_RECONFIGURE_(SHELL|APP) Finally, it clears the memory-mapped interrupt register in the FPGA

Parameters:
  • irq – interrupt value

  • dev – pointer to the reconfiguration device being reconfigured

Returns:

IRQ_HANDLED, indicating interrupt has been acknowledged

reconfig_mem.h

Memory management for reconfiguration: allocating and releasing memory to hold partial bitstreams.

Functions

int alloc_reconfig_buffer(struct reconfig_dev *device, unsigned long n_pages, pid_t pid, uint32_t crid)

Allocates host-side, kernel-space reconfiguration buffers.

In order for partial bitstreams to be loaded onto the FPGA, they need to be written to the ICAP from the driver via PCIe and the XDMA core This function allocates a buffer of sufficient size to hold a partial bitstream for reconfiguration Following this function, the buffer is mapped to the user-space using an mmap call (reconfig_ops.h) Finally, the partial bitstream can then be loaded into this buffer and used to trigger reconfiguration

Parameters:
  • device – reconfig_device for which the bitstream buffer should be allocated

  • n_pages – number of hugepages required to hold the bitsream; calculated in user-space

  • pid – host process ID

  • crid – configuration ID (uniquely identifies the shell bitstream to be loaded)

Returns:

whether target memory allocation completed successfully

int free_reconfig_buffer(struct reconfig_dev *device, uint64_t vaddr, pid_t pid, uint32_t crid)

De-allocates host-side, kernel-space reconfiguration buffer.

Performs the opposite of the function above; to be used when reconfiguration is complete

Parameters:
  • device – reconfig_device for which the bitstream buffer should be allocated

  • vaddr – buffer virtual address

  • pid – host process ID

  • crid – configuration ID

Returns:

always 0; check reconfig_mem.c for explanation

reconfig_ops.h

Standard device operations for the reconfig_dev char device: open, release, ioctl and memory map (mmap)

Functions

int reconfig_dev_open(struct inode *inode, struct file *file)

reconfig_dev open char device

int reconfig_dev_release(struct inode *inode, struct file *file)

reconfig_dev release (close) char device

long reconfig_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

reconfig_dev IOCTL calls

int reconfig_dev_mmap(struct file *file, struct vm_area_struct *vma)

reconfig_dev memory map; maps pages allocated using IOCTL_ALLOC_PR_BUFF from kernel space to user space

pci_util.h

Coyote PCI utility functions.

Functions

inline uint32_t build_u32(uint32_t hi, uint32_t lo)

Utility functions, concatenates two 16-bit values into a single 32-bit value.

void pci_enable_capability(struct pci_dev *pdev, int cmd)

Enables a specific PCIe capability for the device.

Parameters:
  • pdev – Pointer to the PCI device structure

  • capability – Capability to enable

bool msix_capable(struct pci_dev *pdev)

Checks if the PCI device supports MSI-X.

Parameters:

pdev – Pointer to the PCI device structure.

Returns:

true if MSI-X is supported, false otherwise.

pci_xdma.h

Contains functions for loading and setting up the Coyote driver on PCI platforms with the XDMA core.

Functions

void assign_device_id(struct bus_driver_data *data)

Assign a unique ID to each Coyote-enabled FPGA card and set the unique device name.

void vfpga_interrupts_enable(struct bus_driver_data *data)

Enables interrupts issued from vFPGAs.

Interrupts issued through the XDMA core must be enabled By writing to the IRQ Block User Interrupt Enable Mask W1S (0x08) register W1S means “Write 1 to Set”, which means that bits set to 1 in the mask Will enable the corresponding interrupts For more information, refer to the XDMA specification [PG195 (v4.1)] In particular, page 59 onwards, Table 78 and Table 81 for this function

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

void vfpga_interrupts_disable(struct bus_driver_data *data)

Disables interrupts issued from vFPGAs.

Interrupts issued through the XDMA core can be disabled (for e.g., when removing the driver) To do so, it’s necessary to write to the IRQ Block User Interrupt Enable Mask W1C (0x12) register W1C means “Write 1 to Clear”, which means that bits set to 1 in the mask Will disable the corresponding interrupts For more information, refer to the XDMA specification [PG195 (v4.1)] In particular, page 59 onwards, Table 78 and Table 82 for this function

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

void reconfig_interrupt_enable(struct bus_driver_data *data)

Enables interrupts issued for reconfiguration.

Interrupts issued through the XDMA core must be enabled By writing to the IRQ Block User Interrupt Enable Mask W1S (0x08) register The method is the same as for vFPGAs (above), but the mask is different More informaction in the XDMA specification [PG195 (v4.1)], p59 onwards, Tables 78 and 81

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

void reconfig_interrupt_disable(struct bus_driver_data *data)

Disables interrupts used for reconfiguration.

Similar implemenation as in vfpga_interrupts_disable, with a different For more information, see above functions and the XDMA specification, p59 onwards, Tables 78 and 82

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

uint32_t read_interrupts(struct bus_driver_data *data)

Reads the interrupts registers from the XDMA core.

Util function which can be used for debugging but also to flush previous register values For more details on the registers values, refer to the XDMA specification [PG195 (v4.1)], Tables 78, 86, 87

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

Returns:

32-bit value containing the interrupts requests; low 16 are user, top 16 are channel (unused in Coyote)

uint32_t build_vector_reg(uint32_t a, uint32_t b, uint32_t c, uint32_t d)

Utility function, constructs a 32-bit MSI-X vector register value.

This function packs four 5-bit fields (a, b, c, d) into a single 32-bit value. Each field represents an MSI-X table entry index 5 bit-values are used, per the XDMA specification [PG195 (v4.1)], Table 90 onwards

Parameters:
  • a – The first 5-bit field (bits 0–4)

  • b – The second 5-bit field (bits 8–12)

  • c – The third 5-bit field (bits 16–20)

  • d – The fourth 5-bit field (bits 24–28)

Returns:

A 32-bit value representing the packed MSI-X vector register

void write_msix_vectors(struct bus_driver_data *data)

Writes MSI-X vectors to the XDMA configuration registers; per Table 90 in the XDMA specification.

MSI-X vectors are required to uniquely identify an interrupt source and therefore call the correct ISR handler function. In Coyote, we write one vector for each vFPGA device and one for the reconfiguration process In total, there are at most 15 (vFPGA) interrupts and 1 (reconfiguration) interrupt, hence 16 vectors

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

int irq_setup(struct bus_driver_data *data, struct pci_dev *pdev, bool enable_reconfig_irq)

Sets up Coyote IRQs for vfpga and reconfig devices.

This function initializes the MSI-X interrupt vectors for the device and associates them with the appropriate interrupt handler functions (vfpga_isr, reconfig_isr).

Note

enable_reconfig_irq is used to determine whether to set up the reconfiguration interrupt. Additionally, it is used to indicate the “first” set-up of the Coyote driver, when the bitstram is loaded using the Vivado Hardware Manager. When this happens, the MSI-X vectors need to be written to the XDMA configuration registers using the write_msix_vectors function. However, this function is also called after shell_pci_init, which is used when dynamically reconfiguring the shell Then, there is no need to write the MSI-X vectors again, as they are already set up (the XDMA core stays online during shell reconfiguration). However, the IRQ vectors need to be re-initialized for the vFPGA devices (but not for the reconfiguration device).

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • pdev – Pointer to the PCI device structure associated with the Coyote device.

  • enable_reconfig_irq – Boolean flag to enable reconfiguration interrupts.

Returns:

0 on success, negative error code on failure.

void irq_teardown(struct bus_driver_data *data, bool enable_reconfig_irq)

Removes previously set-up IRQs (opposite of irq_setup)

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • enable_reconfig_irq – Boolean flag to disable reconfiguration interrupts; same purpose as in irq_setup.

int pci_check_msix(struct bus_driver_data *data, struct pci_dev *pdev)

Checks if MSI-X is supported and enabled for the PCI device.

This function verifies the presence of MSI-X capability in the PCI device and allocates the required number of MSI-X vectors, which are later used in irq_setup.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • pdev – Pointer to the PCI device structure associated with the Coyote device.

Returns:

0 on success, negative error code on failure.

uint32_t get_engine_channel_id(struct xdma_engine_regs *regs)

Returns the channel ID of the engine; see Tables 41 & 60 in the XDMA specification [PG195 (v4.1)].

uint32_t get_engine_id(struct xdma_engine_regs *regs)

Returns the enginer ID; see Tables 41 & 60 in the XDMA specification [PG195 (v4.1)].

void read_engine_alignments(struct xdma_engine *engine)

Reads from the XDMA aligment registers (address bits and alignment, length granularity) and sets the values in the engine struct.

struct xdma_engine *engine_create(struct bus_driver_data *data, int offs, int c2h, int channel)

Creates a C2H or H2C engine.

Allocates and initializes an XDMA engine structure for either C2H (Card-to-Host) or H2C (Host-to-Card) data transfer. It sets up the engine metadata, including its channel and direction. Additionally, it resets the engine by writing to its control registers.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • offset – Offset of the engine configuration register; obtained from the XDMA specification [PG195 (v4.1)], Table 38

  • c2h – Direction of the engine (1 for C2H, 0 for H2C)

  • channel – Channel number of the engine

Returns:

Pointer to the created engine structure, or NULL on failure

int probe_for_engine(struct bus_driver_data *data, int c2h, int channel)

Probes a single C2H or H2C engine.

This function initializes an XDMA engine for a specific channel and direction. Mostly a wrapper around engine_create, but also performs some sanity checks.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • c2h – Direction of the engine (1 for C2H, 0 for H2C)

  • channel – Channel number of the engine

Returns:

0 on success, negative error code on failure

int probe_engines(struct bus_driver_data *data)

Probes all C2H and H2C engines.

This function iterates through all available channels and initializes XDMA engines for both C2H and H2C directions.

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

Returns:

0 on success, negative error code on failure

void engine_destroy(struct bus_driver_data *data, struct xdma_engine *engine)

Removes a single XDMA engine.

Deallocates resources associated with a specific XDMA engine and resets control registers in hardware.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • engine – Pointer to the XDMA engine structure to be removed

void remove_engines(struct bus_driver_data *data)

Remove all XDMA engines.

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

int map_single_bar(struct bus_driver_data *data, struct pci_dev *pdev, int idx, int curr_idx)

Maps a single BAR (Base Address Register) of the PCI device.

This function maps a specific BAR to the driver’s address space, using the pci_iomap function. Additionally, it includes sanity checks for the BAR type and size.

Note

We need to distinguish between idx and curr_idx, since in Coyote curr_idx goes can be 0, 1, 2 However, idx is the index of the BAR in the PCI device structure, which can be between 0 and 6. If the XDMA core is configure to have 64-bit addresses, then each BAR takes two slots, as explained in Table 3 of the XDMA specification [PG195 (v4.1)].

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information.

  • pdev – Pointer to the PCI device structure.

  • idx – Index of the BAR to be mapped.

  • curr_idx – Index in the driver’s BAR array where the mapped BAR will be stored. This is incremented for each successfully mapped BAR and ensures that the mapped BARs are stored sequentially in the driver’s data structure.

Returns:

0 on success, negative error code on failure.

int map_bars(struct bus_driver_data *data, struct pci_dev *pdev)

Maps all BARs for the Coyote driver.

This function iterates through all available BARs of the PCI device and maps them into the driver’s address space using map_single_bar.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • pdev – Pointer to the PCI device structure

Returns:

0 on success, negative error code on failure.

void unmap_bars(struct bus_driver_data *data, struct pci_dev *pdev)

Unmaps all previously mapped BARs of the PCI device.

Parameters:
  • data – Pointer to the bus driver data structure, containing Coyote device information

  • pdev – Pointer to the PCI device structure.

int shell_pci_init(struct bus_driver_data *data)

(Re-)Initializes the Coyote shell when running on PCI platforms

This function implements a subset of the pci_probe functionality and it should be used to re-initialize the Coyote shell after partial reconfiguration It is called from reconfig_ops.c during the reconfiguration process

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

Returns:

0 on success, negative error code on failure.

void shell_pci_remove(struct bus_driver_data *data)

Clears the state of the Coyote shell when running on PCI platforms.

This function releases the resources and resets the hardware components associated with the shell layer. Only called when the shell is removed through reconfiguration (from reconfig_ops.c).

Parameters:

data – Pointer to the bus driver data structure, containing Coyote device information

int pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)

Top-level PCI initialization function for the Coyote driver.

This function is called during the PCI device enumeration process to initialize the Coyote driver for the detected PCI device. It sets up the necessary resources, mapping the device’s BARs, initializing the XDMA engines, setting up char devices etc.

void pci_remove(struct pci_dev *pdev)

Top-level PCI device removal function for the Coyote driver.

This function is called during the PCI device removal process to clean up the resources and de-initialize the Coyote driver

int pci_init(void)

Top-level entry function, called by coyote_init and simply a wrapper around pci_probe.

void pci_exit(void)

Top-level exit function, called by coyote_exit and simply a wrapper around pci_remove.