对PCIE设备访问及其配置空间的一点理解

讲讲对PCIE总线协议的一点理解吧。感觉每一年又会多一点理解,但不懂得地方仍很多。
PCI总线是拓扑结构,PCI总线从0开始,不超过256(但一般不会一层一层挂太多)。Device不超过32,Function不超过8。如下图,挂在总线0,即Bus 0上的为根(root)设备,下面还挂设备的则为桥(Bridge),不再挂设备的即为设备(Device)。挂在桥下的设备总线号必然大于桥的总线号,下图中,PCI桥片1位Bus 0,PCI设备11为bus 1,PCI设备31为Bus 3。所以PCI桥片1的从属总线是1-3。
在这里插入图片描述
挂在PCI总线上的所有桥或设备都有特定的编号,即为Bus,Device,Function,不会重复。CPU对于挂在root上的设备都有固定定义,查看datasheet即可。
在这里插入图片描述
PCI设备的配置空间如下图:
在这里插入图片描述
该空间寄存器的详细信息可查看PCIE Spec,以Class Code为例,Class Code是判断PCI类型:LAN、VGA、存储设备等等。对于下面一段代码进行分析。

 if (Hdr->ClassCode[2] == 0x0C) {
            if (Hdr->ClassCode[1] < sizeof (gPciSerialClassCodes) / sizeof (VOID *)) {
              Str = gPciSerialClassCodes[Hdr->ClassCode[1]];

              if (Hdr->ClassCode[1] == 0x03) {
                switch (Hdr->ClassCode[0]) {
                case 0x00:
                  Str = L"USB-UHCI               ";
                  break;
                case 0x10:
                  Str = L"USB-OHCI               ";
                  break;
                case 0x20:
                  Str = L"USB-EHCI               ";
                  break;
                default:
                  break;
                }
              }
            }
          }

首先要判断Class Code的高位为0x0c(串行总线控制器),才能找到0x0c对应的Sub-Class Code,代码对应下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如何访问PCI设备?
两种方式:IO或memory
IO:即地址端口0xcf8/数据端口0xcfc,特定Bus,Device,Function按下图方式得到地址(实际中寄存器地址不用偏移两位),写入0xcf8;从0xcfc得到数据。
在这里插入图片描述
memory:方式类似,但Bus,Device,Function全部左移四位,bit31-28也根据CPU不同而不同,以Intel为例:Address = 0xE0000000+(Bus<<20)+(Device<<15)+(Function<<12)+Register.**(这里有点忘了,不确定!)o(╥﹏╥)o
以此可以看出IO方式只能访问256字节,即为PCI的配置空间。
PCI和PCIE的不同?
每种function包含4K的配置空间,前256字节为兼容配置空间,PCI的配置空间为0x00- 0xFF,PCIe设备还支持0x100 -0xFFF这段扩展配置空间。
怎么判断是PCI还是PCIE?
PCIE扩展空间的头指针存放于Capability Pointer(Config_ddress+0x34),从偏移地址0x34开始,读取值,该值为指向下一个ID的指针,判断ID是否为0x10,不是则读取指针+1的寄存器的值,该值为下一个ID的指针,直到ID为0x10,,当ID为0x10时,则该设备为PCIE设备,此ID开始的地方为PCIE Capability结构的开始,PCI设备不能使用这段空间。当Pointer为0,结束。
原话:PCI Express Capability ID Register
This read-only field must contain the value 10h,indicating this is the start of the PCI Express Capability register set

在这里插入图片描述
举个栗子:如下图,地址0x70开始为PCIE Capability结构配置空间
在这里插入图片描述
该空间的具体寄存器信息如下,里面包含设备种类(上下游)、负载、PCIE Link速度,Link宽度等等。
在这里插入图片描述
以速度和宽度举例:
在这里插入图片描述
下列代码是判断该PCIE速度(Gen1/Gen2/Gen3)和宽度(x1/x2/x4/x8/x16/x32).

// check is pcie capability
  Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint8, PcieCapabilityPtr, 1, &CapabilityId);
  LinkStatusPtr = (UINT16)(PcieCapabilityPtr + 0x0C);
  Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint16, LinkStatusPtr, 1, &LinkStatusValue);
  GenValue = (UINT8)(LinkStatusValue & 0xF);
  LaneValue = (UINT8)((LinkStatusValue & 0x3FF) >> 4);

  switch (GenValue) {
  case PCIE_GEN1:
    GenStr = L"Gen1";
    break;
  case PCIE_GEN2:
    GenStr = L"Gen2";
    break;
  case PCIE_GEN3:
    GenStr = L"Gen3";
    break;
  default:
    break;
  }

  switch (LaneValue) {
  case PCIE_LANEX1:
    LaneStr = L"Lane:1";
    break;
  case PCIE_LANEX2:
    LaneStr = L"Lane:2";
    break;
  case PCIE_LANEX4:
    LaneStr = L"Lane:4";
    break;
  case PCIE_LANEX8:
    LaneStr = L"Lane:8";
    break;
  case PCIE_LANEX12:
    LaneStr = L"Lane:12";
    break;
  case PCIE_LANEX16:
    LaneStr = L"Lane:16";
    break;
  case PCIE_LANEX32:
    LaneStr = L"Lane:32";
    break;
  default:
    break;
  }

另:附上遍历主板所有PCI设备并判断是PCI还是PCIE设备的代码(将设备的地址送入0xCF8,从数据端0xCFC读出来的vendor值为0xFFFF,即为无效,没有设备。)
注:PCI空间是以double word读取,字节和字读取都无效。

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

void delay(unsigned int max);
void PCIE_SCAN(unsigned int Address);

int main()
{
	char bus,device,function;
    unsigned int cfg_add,ventor_val;
	for(char i=0;i<5;i++)       //scan bus
	{
		for(char j=0;j<32;j++)      //scan device
		{
		   for(char k=0;k<8;k++)      //scan function
		   {
			   bus = i;device = j;function = k;
			   // the base address of every device
               cfg_add = 0x80000000+bus*0x10000+(device*8)*0x100+function*0x100;    
			   outpd(0xCF8,cfg_add);
			   // vendor number
               ventor_val = inpd(0xCFC);
			   //judge the device exist or not
			   if(ventor_val!=0xffffffff)
			   {
			      printf("%04x  ",ventor_val);
				   printf("bus = %02x,device = %02x,function = %02x",bus,device,function);
				   delay(100);
				   //find PCIE device
                   PCIE_SCAN(cfg_add);
			   }
		   }
		       
		}
	}
	system("pause");
	return 0;
}
void delay(unsigned int max)
{
    unsigned int d;
    for(d=0;d<max;d++);
}
void PCIE_SCAN(unsigned int Address)
{
  	int flag =1;
	unsigned int capability_pointer,capability_ID,capability_Npointer,p;
      // printf("%0x",Address);
	                //capability pointer register is 0x34
                    outpd(0xCF8,(Address+0x34));
					capability_pointer = inp(0xCFC);
					while(flag)
					{
						
						 outpd(0xCF8,(Address+capability_pointer));
						 //capability ID and next pointer and other 16 bits
						 p = inpd(0xCFC);
                         capability_ID = p%256;
						 if(capability_ID==0x10)
						 {
							 printf("  PCIE device\n");
							 goto ex;
						 }
						 p = p>>8;
						 capability_Npointer = p%256;
						 //When pointer is 0x00,there is no space of extension
						 if(capability_Npointer==0)
						 {
							 flag = 0;
						 }
						 capability_pointer = capability_Npointer;
					}
					printf("  PCI device\n");
ex:                 ;
}

在DOS下运行结果如下图:
在这里插入图片描述

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值