Web resources

The official PCI web site is located at www.pcisig.com

This PCI Special Interest Group site contains a little information on PCI as Microsoft Word 6 document files which are the official specifications, it also contains a few test/diagnostic programs in C for working with PCI that you can download and also they keep a list of Vendor ID numbers but not product ID numbers.

Recommended books on PCI Programming

The two books below are based on the PCI spec and supply much information about device types, interrupt rerouting, Hot-plug /Power management specs etc.

PCI System Architecture - from Mindshare Inc
                          by Tom Shanley and Don Anderson
                          ISBN 0-201-30974-2 ($US 40 in 2004)
                          publisher Addison Wesley

PCI-X System Architecture - from Mindshare Inc
                          by Tom Shanley
                          ISBN 0-201-72682-3 ($US 45 in 2004)
                          publisher Addison Wesley

What can i expect from the PCI docs ?

The PCI documentation tells us how one can list the PCI hardware (both extension card and built-in components such as hard drive controllers, PCI-to-PCMCIA bridges, etc.) and what resources that hardware use for communication with the CPU. However, how you should use those resources to program the hardware depends on the very hardware you try to program (i.e. PCI has no info about how AC97 pci soundcard works: it can just tell you that it is a soundcard and that it will use memory-mapped region [ 0x1234..0x5678 ] and interrupt 10 to communicate with you.

Do I have to pay $xxx for an open-standard specification

Official PCI documents (PCI_Local_Bus_Spec_v2.x.pdf) unfortunately come at a high fee. Free-pirate hosting links are not welcome here (of course). Before ordering the documents, keep in mind that they're describing the whole standard, not just the programming information you need. That means you'd end up with a 300+ pages hard-cover book full of electric & mechanics specifications, bus timing, burst frame structure etc. which are completely meaningless for you.


What should a 'PCI core' component offer ?

Primitives for accessing configuration space

Quoting 'PCICFG.TXT' from RalfBrown PCI utility
PCI devices have an address which is broken down into a PCI-bus number (usually 0), a device number within that bus (0-31), and a function number within the device (0-7). (...) There are two incompatible methods for accessing the ports; on most chipsets, -b1 is the proper method ("PCI Configuration Space Access Mechanism #1"). On some chipsets (mostly the earliest ones supporting PCI), you will need to use -b2 ("PCI Configuration Space Access Mechanism #2"). USING THE INCORRECT METHOD CAN HANG OR RESET YOUR COMPUTER!, which explains why using BIOS service for accessing PCI makes sense ...

Configuration mode 2 is deprecated, afaik, so it won't be covered here. The following code snippet (under RalfBrown tutorial license ;) reads the value of register reg on bus:device:func pci space. Accessing a datum in PCI mechanism 1 consist of sending its 'address' on port 0xCF8 and then either reading or writing the value through port 0xcfc.

    DWORD addr = 0x80000000L | (((DWORD)(bus & 0xFF)) << 16) |
                      ((((unsigned)device) & 0x1F) << 11) |
                      ((((unsigned)func) & 0x07) << 8) | (reg & 0xFC) ;
         DWORD orig = inp(0xCF8) ;      // get current state
         outpd(0xCF8,addr) ;            // set up addressing to config data
         value = inpd(0xCFC) ;          // get requested DWORD of config data
         outpd(0xCF8,orig) ;            // restore configuration control

note: PCI bios interface do have them

Bus enumeration

There are two possible approach for device/driver handling:

driver push
the user tells the system to use the driver and the system check there's a device here. This check requires that you scan devices, looking for a specific vendor/subclass.
driver pull
the system scans for devices and loads drivers for devices that exists.

So in both approach, enumeration is required. The basics of devices enumeration consists of probing each bus:device:function in the configuration space and see if we have something consistent.

Note that devices does not necessarily have contiguous numbers. E.g. a bus may have device 4 present even if there are no devices 1, 2, or 3. This is especially common on the bus #0 due to on-chip devices.


// disclaimer: this is untested code summarizing
// http://my.execpc.com/~geezer/osd/pnp/pci32.c

// it doesn't take pci-to-pci bridges into account
// and blindly scans all the possible busses.


int nb_bus=1;

for (bus=0;bus<nb_bus;bus++) {
  for (dev=0;dev<64;dev++) {
    byte htype=pciGetCfgByte(bus,dev,fn,PCI_HEADER_TYPE);
    word vendor_id=pciGetCfgWord(bus,dev,0,PCI_VENDORID);
    int nbfuncs=1;

    if (vendor_id==0 || vendor_id==0xffff) continue;
    if (htype & 0x80) nbfuncs=8;

    for (fn=0;fn<nbfuncs;fn++) {

      /** we have a device. Get vendor (0x00:2), product (0x02:2), class (0x09:3)
       *  information and create a DeviceNfo with it for later retrieval
       *  Don't forget to insert bus:dev:fn in the DeviceNfo so that we don't need
       *  to bother with enumeration again
       */
    }
  }
}

Primitives for decoding BARs

Address registers (telling what memory range or I/O range is used by the device) may not be immediate to use. According to sigops's chapter, the lowest bit you read will tell you if you're facing memory (e.g. bit0==0) or IO resource (e.g. bit0==1). You can tell the size of the region by writing 0xffffffff in the BAR and reading back. Once you're done, don't forget to write back the old value.

void pciDecodeBar(pci_device* pci, pci_bar_register bar)
{
   unsigned type = pciReadDword(pci, bar) & 0x0f;
   unsigned addr = pciReadDword(pci, bar) & ~0x0f;
   unsigned size;

   pciWriteDword(pci, bar, 0xffffffff);
   size = pciReadDword(pci, bar) & 0xfffffff0;
   pciWriteDword(pci, bar, addr|type);

   size = 1 + ~size;

   kprint(((type&1)?"memory %8x..%8x, %s":"i/o %4x..%4x"),
          addr,addr+size,(type&8)?"prefetchable":"no prefetch");
}

``BARs'' ? You mean i should go for a drink ?

For auto-configuration, PCI has has a special space called "PCI Configuration Space". This space is accessed using I/O ports. There's 2 different configuration space access mechanisms, but for the most common one you set the address in one I/O port (the bus, device, function and offset) and read or write the data in another I/O port.

Anyway, in each PCI device's configuration space there's normally one or more BARs (or "Base Address Registers"), which can be used to set or find the address (in physical memory or in I/O space) for each resource the card uses.

The BARs could be used for memory mapped I/O, I/O port ranges and/or a boot ROM.

They aren't used for IRQs though - there's different fields in PCI configuration space for that, one for the interrupt line the card uses (which is a fixed/static flag) and another for which IRQ it is routed to (which is set by the BIOS during boot, and only exists as a means to pass this information to operating systems - the hardware itself ignores it).


Categories: CollectedKnowledge, HardWareBus


related threads

PCI related , calling BIOS32 service, finding PCI device, Clicker 0.8.20 -- pci

most of the links posted on these threads looks broken, though (unfortunately)

additionnal material

there was an article called "pentium_vme_article.pdf" formerly on the Web, but it disappeared. PciSectionOfPentiumVme is revived here ...