Documentation

PCI Bus Considerations

Introduction

When writing Simulink® Real-Time™ drivers for PCI devices, consider the memory access method. A PCI device can be either I/O port mapped or memory mapped.

  • I/O port mapped — The BIOS assigns a port range.

  • Memory mapped — The BIOS assigns a memory region, if your device is memory mapped.

The computer BIOS automatically assigns a conflict-free set of resources to the PCI devices found in the system at boot-up. You typically do not know where the board resides (base address) before driver initialization. However, you can obtain this information by querying the PCI configuration space at run time. The Simulink Real-Time software provides functions to accomplish this.

To locate a PCI device, you need the following:

  • Vendor and device ID

  • Optionally, subsystem vendor and subsystem device ID

      Note:   You need the subsystem vendor and subsystem device ID if the vendor and device ID do not uniquely identify the board.

  • Slot number or bus and slot number

You can have the drivers locate PCI devices in one of the following ways:

  • If the system has one board of a given type, you can use the driver slot option to search for the first board that matches a vendor and device ID. To initiate this search, set this option to -1.

  • If the system contains multiple boards of the same type, setting the slot option to -1 does not find the additional boards. In that case, specify the bus and slot numbers with the vendor and device IDs.

PCI Configuration Space API

Before you can access a PCI device, you need to access the configuration space to locate the board in the target computer memory. This section describes the procedure to do this.

For PCI devices, the driver will need to access the PCI configuration space for the board. This space contains relevant board information such as the base address and access type (I/O port or memory mapped). The Simulink Real-Time software provides functions that allow the driver to access this space.

  • Vendor and device ID — The driver searches the boards for the specified vendor (manufacturer) and device ID. The PCI Steering Committee, an independent standards body, assigns a unique vendor ID (uint16) to each PCI board vendor. Each vendor then assigns a unique ID to each PCI board type it supports.

      Note:   Vendor and device IDs might not uniquely identify a board. For example, boards that use the PLX-9080 bus interface chip have a vendor ID of 10B5 (the vendor ID assigned to PLX Technology, Inc.). The device ID for the chip is 9080. In cases like this, to select a particular board that contains this chip, you must use a subvendor and subdevice ID in addition to the vendor and device IDs.

  • Slot number or bus and slot number — The driver looks only for the board that matches the specified vendor and device ID and slot number.

PCI Device Information

Use the xpcGetPCIDeviceInfo function to get information for a PCI device in your system. The syntax for this function is:

int xpcGetPCIDeviceInfo (uint16_T vendorId, uint16_T deviceId, uint16_T subVendorId, uint16_T subDeviceId, uint16_T bus, uint16_T slot, xpcPCIDevice *pciInfo);

This function returns the xpcPCIDevice structure filled according to the following:

If You Supply...This Function....
All four parametersLooks for a device that matches all four parameters and returns the xpcPCIDevice structure for that device. Use this form if you know that your system has multiple boards from the same vendor with the same ID and you want your user to specify the exact device.
XPC_NO_SUB for the subVendorId or subDeviceId parameterDoes not consider the subvendor or subdevice ID when looking for a match for the specified device. It returns the xpcPCIDevice structure for a device that matches the other parameters. You can use this form if you do not plan to use the subVendorId or subDeviceId values.
XPC_NO_BUS_SLOT for the slot for the deviceReturns the first PCI device it finds that matches the remaining parameters. You can use this form if you know that your system has only one board with a particular ID set.

Passing Slot Information from the Block Mask to Its Driver

Simulink Real-Time drivers use the following convention to fill in slot parameters and retrieve slot information. Choose the convention that will work best for you.

Set...To...

Set slot = -1

Assume bus = 0 and call the xpcGetPCIDeviceInfo function to find the first instance of the board.

Set slot = S

Assume bus = 0 and call the xpcGetPCIDeviceInfo function to find the specified board. If the board matches the IDs, return the PCI information to the driver. Otherwise, return an error.

Set slot = [B, S]

Check bus B and slot S for the specified board. If the board matches the IDs, return the PCI information to the driver. Otherwise, return an error.

Setting slot = [0, S] is identical to slot = S.

The following example illustrates how to use the xpcGetPCIDeviceInfo function to program the driver to accept slot number input or slot and bus number input from the driver block.

  1. Call this function from the mdlStart callback function.

  2. Pass the slot number or slot and bus number into the xpcGetPCIDeviceInfo function using code like the following:

    uint16_T       vendorId, deviceId;
    int32_T        bus, slot, subvendor, subdevice;
    xpcPCIDevice   pciInfo;
    
    /* S_PCI_SLOT_ARG is passed in from the mask */
    /* Typically the slot arg is a scalar containing -1 if the target 
    has only one board of this type */
    /* If the target has multiple boards of this typem, the slot arg 
    is a vector containing bus and slot info */
    /* This code snipped parses the slot arg into bus and slot */
    if ( (int_T)(mxGetN(ssGetSFcnParam(S, S_PCI_SLOT_ARG))) == 1 ) {
    	bus  = 0;
    	slot = (int32_T)(mxGetPr(ssGetSFcnParam(S, S_PCI_SLOT_ARG))[0]);
    } else {
    	bus  = (int32_T)(mxGetPr(ssGetSFcnParam(S, S_PCI_SLOT_ARG))[0]);
    	slot = (int32_T)(mxGetPr(ssGetSFcnParam(S, S_PCI_SLOT_ARG))[1]);
    }
    
    vendorId = (uint16_T)0x1234;
    deviceId = (uint16_T)0x9876;
    subvendor = (uint16_T)0x5678;
    subdevice = (uint16_T)0x8765;
    /* Set subvendor and subdevice to XPC_NO_SUB, XPC_NO_SUB if they are not required */
    
    /* xpcGetPCIDeviceInfo() populates the pciInfo struct */
    if ( xpcGetPCIDeviceInfo(vendorId, deviceId,
    					subvendor, subdevice,
    					bus, slot,
    					&pciInfo) ) {
    	sprintf(msg, "Board 0x%x not found at bus %d slot %d", deviceId, bus, slot);
    	ssSetErrorStatus(S, msg);
    	return;
    }

For detailed information on the xpcPCIDevice structure, see xpcPCIDevice.

Memory-Mapped Accesses

A memory-mapped PCI board uses up to six memory regions to access board regions and memory. Each region might also have a different length. You must call the xpcReserveMemoryRegion function for each PCI memory region you want to access; use the returned virtual address to access the region. Failure to do this will result in a segmentation fault.

To access a memory mapped location, do the following:

  1. Declare a variable of the required pointer type to hold the memory location. For example:

    volatile uint32 *csr; /* Control and status register */

      Note:   Use the volatile keyword here; otherwise, the compiler might optimize away accesses to this location.

  2. Set the pointer value (address) to the physical address at which the register resides.

I/O Port Accesses

To access I/O ports, use the following functions:

Was this topic helpful?