ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [컴퓨터 구조] PCI
    공학/컴퓨터구조 2023. 8. 9. 20:42

    글의 참고

    - https://wiki.osdev.org/PCI

    - https://en.wikipedia.org/wiki/PCI_configuration_space

    https://naveenaidu.dev/pci-devices-summary

    https://resources.infosecinstitute.com/topic/system-address-map-initialization-in-x86x64-architecture-part-1-pci-based-systems/

    https://www.linkedin.com/pulse/pci-express-primer-1-overview-physical-layer-simon-southwell

    https://stackoverflow.com/questions/19223394/what-is-the-function-number-in-pci

    https://community.osr.com/discussion/155585

    https://stackoverflow.com/questions/37901128/understanding-pci-address-mapping

    - https://wiki.qemu.org/images/f/f6/PCIvsPCIe.pdf

    https://pdos.csail.mit.edu/6.828/2008/readings/i386/s08_01.htm

    - http://nimhaplz.egloos.com/5314763

    https://stackoverflow.com/questions/19006632/how-is-a-pci-pcie-bar-size-determined

    https://www.bit-basics.com/system-architecture-and-pcie-basics/

    - http://egloos.zum.com/nimhaplz/v/5314763


    글의 전제

    - 내가 글을 쓰다가 궁금한 점은 파란색 볼드체로 표현했다. 나도 모르기 때문에 나중에 알아봐야 할 내용이라는 뜻이다.

    - 밑줄로 작성된 글은 좀 더 긴 설명이 필요해서 친 것이다. 그러므로, 밑 줄 처친 글이 이해가 안간다면 링크를 따라서 관련 내용을 공부하자.

    - `글의 참조`에서 빨간색 볼드체로 체크된 링크는 이 글을 작성하면 가장 많이 참조한 링크다.

    - 대개 UEFI 에서 말하는 platform은 hardwares를 의미한다. 근데 구체적인 특정 하드웨어를 의미하기 보다는 Chipset 및 SoC를 의미하는 경우가 많다.


    글의 내용

    - PCI Address

    : PCI 에서 사용하는 주소 영역은 3개다. 왜 3개나 사용할까? 2가지 용도가 있다. 한 가지 용도는 데이터 통신용이다. 즉, PCI 디바이스들과 통신하기 위해서 RAM 혹은 I/O 메모리를 사용한다. 두 번째 용도는 PCI 디바이스 설정을 위해서다. Configuration Space는 PCI 디바이스 설정을 위해서 사용한다. 

    1" I/O Address
    2" Memory Address
    3" Configuration Space

    : I/O 어드레스는 x86의 산물이고, Memory Address는 MMIO를 의미한다. 이 2개는 물리적 각각 `I/O 메모리`와 `RAM`에 매핑된다. 아래 그림은 클래식 PCI가 사용하는 3가지 주소 공간들을 보여준다. 중간에 I/O 주소 공간은 현재는 거의 레거시라고 봐도 무방하다. 

    출처 - https://wiki.qemu.org/images/f/f6/PCIvsPCIe.pdf

     

    : `PCI Configuration Space` 영역은 논리적 개념이다. 즉, RAM 이나 I/O 메모리와 같이 물리적으로 별도의 `PCI Configuration Space`라는 물리적 메모리가 존재하는 것은 아니다. 전통적으로 MMIO 라고하면, 대개는 I/O 디바이스 레지스터가 RAM에 1:1 매핑되는 `직접 접근`을 했다. 예를 들면, 아래 그림처럼 말이다. 아래, 그림을 보면, RAM과 ADC의 특정 레지스터들이 직접 대응하여, CPU에서 0x1FF0F2D2로 접근을 하면, ADC의 `Data Register` 에 접근하는 것과 같다. 그런데, PCI는 여러 개의 디바이스들을 모두 호환해야 버스다. 그러다 보니, 모든 디바이스들을 표현할 수 있는 자료 구조를 반드시 동시에 그 자료 구조를 통해서 각 PCI 디바이스들을 구분할 수 있어야 한다. 이 자료 구조가 바로 `PCI Configuration Space`다.

    : `PCI Configuration Space`의 공통 헤더는 아래와 같은 포맷을 따른다. `Device ID`, `Vendor ID` 등을 모두 `PCI Configuration Register`라고 부른다. `PCI Configuration Space`의 공통 헤더에는 총 10개의 `PCI Configuration Register`가 존재한다. 이렇게 `PCI Configuration Space`란 결국,  `PCI Configuration Register`들의 집합과 같다. 그럼 여기서 질문. 아래 레지스터들을 모든 PCI 디바이스들이 가지고 있을까? 그렇지 않다. 저 내용을 PCI 디바이스를 개발하는 드라이버 개발자가 만들어서 PCI 버스에 등록해야 한다. 예를 들어, 위에서 ADC 드라이버 개발자는 PCI 버스에 ADC를 장착시킬 거면, ADC의 정보들을 모아서 `PCI Configuration Space`를 만들어서 PCI 버스에 건내줘야 한다.

    : `PCI Configuration Space`는 RAM에 MMIO로 할당된다. x86 기반의 예전 PC라면 Port I/O로 접근이 가능하다. 각 PCI Function들 마다 각자만의 `PCI Configuration Space`가 존재한다. A라는 PCI Function의 `PCI Configuration Space`에 접근하기 위해서는 A의 `PCI Configuration Space` 가 할당된 RAM의 시작 주소를 알아야 한다. 이 주소는 어떻게 알아낼까? `PCI Configuration Space` 주소를 정하는 방식은 `geographically` 하게 정해진다. 무슨 말이냐면, PCI 디바이스가 실제 마더 보드상에 어느 슬롯에 꽂히냐에 따라서 Configuration Space 주소(물리적 주소)가 정해진 다는 것이다. 왜 그럴까? Configuration Space 주소는 32비트를 가지며 3가지 요소에 의해 결정된다. 바로 `BUS/DEVICE/FUNCTION` 이다. 이걸 BDF 번호라고 한다.

    : PCI Confguration Space는 위의 3가지에 의해서 확정된다. 예를 들어, 하나의 마더 보드가 있다. 여기에 2개의 PCI 버스가 있고, PCI 슬롯은 8개가 있다고 치자. 그래픽 카드가 하나를 PCI 버스에 장착하려고 한다. PCI 버스 0번에 붙이고, PCI 슬랏 4번에 꽂을 려고 한다. 그러면 해당 그래픽 카드의 Configuration Space는 다음과 같아진다.

     

    0b10000000_00000000_00004/000_${오프셋}/00

     

    : 위에서 붉은색으로 표시된 부분들이 PCI 디바이스 Configuration Space 주소를 결정한다. 즉, 저 주소는 PCI 디바이스가 어디 버스에, 어디 슬롯에 붙냐에 따라 결정된다. 즉 `버스 번호`와 `디바이스(슬랏) 번호`에 따라 Configuration Space의 주소(물리적 주소)는 정해진다. 예를 하나 더 들자. 사운드 카드가 하나를 PCI 버스에 장착하려고 한다. PCI 버스 1번에 붙이고, PCI 슬랏 5번에 꽂을 려고 한다. 그러면 해당 사운드 카드의 Configuration Space는 다음과 같아진다.

     

    0b10000000_00000001_00005/000_${오프셋}/00

     

    : 지금까지 각 PCI 디바이스가 할당받는 주소에 대해 알아봤다. 근데, FUNCTION은 사용하지 않는 것인가? PCI 디바이스는 총 8개의 FUNCTION을 가질 수 있다. 즉, 위의 사운드 카드를 예를 들어보자면, 아래와 같다.

    1" 0b10000000_00000001_00005/000_${오프셋}/00
    2" 0b10000000_00000001_00005/001_${오프셋}/00
    ...
    8" 0b10000000_00000001_00005/007_${오프셋}/00

    : 이 말은 PCI 디바이스 하나가 가질 수 있는 Configuration Space 주소가 8개란 소리다(Configuration Space - 256B). 그런데, FUNCTION이 이렇게 많아서 뭐할까? FUNCTION이란 `기능`이다. 즉, 하나의 PCI 디바이스가 8가지 기능을 가질 수 있다는 소리다. 

     

    : 예를 들어, PMIC는 ADC로 동작할 수도 있고, Power supply로도 동작할 수 있다. 이 말은 하나의 물리 디바이스가 SW를 통해 논리적으로 여러 디바이스로 보이게 만드는 것이다. 마치 1TB 물리적인 하드 디스크를 512GB 논리 디스크 2개로 나눈다고 생각하면 된다. 오프셋은 바로 뒤에서 설명한다.

     

    - Configuration Space

    : PCI Configuration Space는 PCI 버스에서 PCI 컨트롤러가 각 PCI 디바이스를 구별해주는 식별자다. Configuration Space 안에는 PCI 디바이스를 식별하고 컨트롤할 수 있는 모든 정보가 포함되어 있다. Configuration space 총 256B 이며 2개의 파트로 나눌 수 있다.

    1) Predefined header regison(64B)
    2) Device dependent region(192B)

     

    : 스펙에서 `device dependent region`에 대한 설명을 생략하고 있다. 간략하게 나마 알아본 바로는 각 디바이스 회사에서 필요하면 predefined header 뒤쪽에 device specific registers를 놓아야 한다고 한다. 이 영역은 predefined header의 capabilities pointer 필드와 관련이 있다. `Capability`에서 얘기하도록 한다.

     

    : Predefined Header는 또 2개의 파트로 나뉜다.

    1) Common Header (16B)
    2) Device Specific Header (48B)


    Configuration Common Header 출처 - PCI 3.0 revision specification

     

    : 위의 그림이 모든 PCI 디바이스들이 가지는 공통 헤더(16B)이다. 나머지 뒤에 48바이트는 공통 헤더에 들어있는 헤더 타입 필드에 따라 헤더 구성이 조금 달라진다. 일단 공통 헤더에서 Vender ID, Device ID, Command, Status, Revision ID, Class Code, Header Type은 필수적으로 구현해야 한다. 헤더 타입 필드(위치 - 0Eh)는 PCI 3.0 revision specification에서는 총 3개가 명시되어 있다.

    1) Type 0x0 - PCI Devie
    2) Type 0x1 - PCI-to-PCI Bridge
    3) Type 0x2 - PCI-to-CardBus Bridge

    : 아래의 구성은 각 헤더타입에 따라 달라지는 `Device Specific Header`의 모습이다. 사실 주로 사용하는 건 실제 기능을 보여주는 `PCI 엔드 포인트 디바이스(Header Type 0x0)일 것이다. `Bridge`는 기능 보다는 대개 PCI 버스들을 `연결`하는 용도로 사용된다.

     

    Header Type 0x0 Header Type 0x1 Header Type 0x2 

    : 이제 Configuration Space의 중요한 몇 가지 공통 헤더 필드에 대해 알아보자.

    1" Device ID(DID) & Vendor ID(VID) : PCI 디바이스를 식별해준다. 즉, 이 필드들을 통해서 이 디바이스가 어떤 기능을 하는 디바이스인지를 알게 된다. 예를 들어, 사운드 카드 디바이스인지, 네트워크 카드 디바이스인지, SATA 디바이스인지 등을 이 2개의 필드로 알아 낸다. VENDOR ID는 PIC-SIG에서 회사마다 정해놓은 고정값이 있다. 예를 들어, intel은 `0x8086`을 할당받았고, 마이크로소프트는 `0x1414`를 할당받았다. DEVICE ID는 회사에서 자신들에 제품에 부여하는 번호이다. 예를 들어, 인텔은 `SATA Controller(AHCI)`에 `0xA0D3`을 부여했다.
    2" Status : Status 레지스터는 해당 PCI 디바이스의 상태를 표시한다(ATA Status 레지스터와 동일하다고 보면 된다). 
    3" Command : 해당 디바이스의 인터럽트를 컨트롤하거나, 버스 마스터를 요청하거나 하는 것들을 이 레지스터를 통해서 할 수 있다.
    4" Header Type : 위에서 이미 설명.
    5" Class Code : Vendor ID와 Device ID가 `디바이스`에 대한 설명이 었다면, Class Code는 `기능`에 대한 설명을 해준다. 사실 이 클래스 코드는 3B로 명시되어 있지만, 디테일하게는 1B씩 3개의 영역으로 나뉜다. 

    : Configuration Space도 간략하게 나마 알아봤으니, 이제 여기에 접근하는 방법을 알아보자. 32비트 x86 아키텍처 기반으로 설명한다. 그리고 PCI 3.0을 기준으로 설명한다. 참고로, PCIe 부터는 MMIO 방식으로 바뀌었다. 

     

    : 2개의 I/O 주소를 사용해서 Configuration Space에 접근할 것이다. IBM PC 호환 기종에서 PCI Configuration Space는 주소 공간은 0xCF8(CONFIG_ADDRESS), 데이터 공간은 0xCFC(CONFIG_DATA)로 고정되어 있다. 접근법은 간단하다. 접근하고 싶은 PCI 디바이스의 Configuration Space 주소를 0xCF8에 쓰고, `out` 명령어로 보낸다. 그리고 `in` 명령어에 0xCFC를 줘서 데이터를 받으면 된다.

    Two DWORD I/O locations are used to generate configuration transactions for PC-AT compatible systems. The first DWORD location (`CF8h`) references a read/write register that is named `CONFIG_ADDRESS`. The second DWORD address (`CFCh`) references a read/write register named `CONFIG_DATA`. The CONFIG_ADDRESS register is 32 bits with the format shown in Figure 3-2. Bit 31 is an enable flag for determining when accesses to CONFIG_DATA are to be translated to configuration transactions on the PCI bus. Bits 30 to 24 are reserved, read-only, and must return 0's when read. Bits 23 through 16 choose a specific PCI bus in the system. Bits 15 through 11 choose a specific device on the bus. Bits 10 through 8 choose a specific function in a device (if the device supports multiple functions). Bits 7 through 2 choose a DWORD in the device's Configuration Space. Bits 1 and 0 are read-only and must return 0's when read.

    - 참고 : PCI_Rev_30.pdf

     

    : PCI 디바이스의 Configuration Space 주소가 뭐지? 위에서 설명했었다. `BUS/DEVICE/FUNCTION` 번호를 합치면 나오는 숫자가 Configuration Space의 베이스 주소다. 그리고 오프셋이 남았다. 이 오프셋은 말 그대로 Configuration Space의 어떤 레지스터에 접근할 것인지를 명시한다. 아래의 코드를 보자.

    uint16_t pciConfigReadWord(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
        uint32_t address;
        uint32_t lbus  = (uint32_t)bus;
        uint32_t lslot = (uint32_t)slot;
        uint32_t lfunc = (uint32_t)func;
        uint16_t tmp = 0;
     
        // Create configuration address as per Figure 1
        address = (uint32_t)((lbus << 16) | (lslot << 11) |
                  (lfunc << 8) | (offset & 0xFC) | ((uint32_t)0x80000000));
     
        // Write out the address
        outl(0xCF8, address);
        // Read in the data
        // (offset & 2) * 8) = 0 will choose the first word of the 32-bit register
        tmp = (uint16_t)((inl(0xCFC) >> ((offset & 2) * 8)) & 0xFFFF);
        return tmp;
    }

    : 근데 오프셋을 상위 6비트만 사용하고 있다. 오프셋을 6-bit만 쓰는 이유는 4바이트 정렬을 위해서이다. 마지막 2비트가 00임 유지함으로써, 0x04, 0x08, 0x0C, 0x10 ... 0xFF 식으로 접근이 된다. 오프셋를 통해서 PCI Function의 Configuration Space에 접근해서 레지스터의 값을 얻어내는 것이다. 이제 중요한 `Base Address` 필드이다. 

     

    - Base Address

    : PCI 버스에 디바이스가 장착되었다. 이 디바이스는 이제 PCI 디바이스가 되었다. 그런데, 이 디바이스와 통신하고 싶다. 어떻게 해야할까? 이 PCI 디바이스가 통신하려면 이 디바이스를 PCI 버스내에서 식별할 수 있어야 한다. 그래서 PCI 버스는 자신에게 붙어있는 각각의 PCI 다비이스들에게 주소를 할당한다. 이 주소는 x86 아키텍처 같은 경우에는 MMIO 쪽으로 할당될 수도 있고, PMIO쪽으로 할당될 수 도 있다. 이러한 주소 할당은 시스템 펌웨어(UEIF 혹 BIOS, OS)가 담당한다. 시스템 부팅 시점에, 시스템 펌웨어는 `지리적 위치(PCI 버스와 마더 보드상 PCI 슬랏 위치)`를 기준으로 PCI 디바이스에게 주소를 할당한다. 이 때, 슬랏을 선정할 때 PCI 컨트롤러가 연결되어 있는 `IDSEL` 라인을 통해서 시스템 펌웨어가 PCI 디바이스의 순서를 정할 수 있게 된다.

    To address a PCI device, it must be enabled by being mapped into the system's I/O port address space or memory-mapped address space. The system's firmware (e.g. BIOS) or the operating system program the Base Address Registers (commonly called BARs) to inform the device of its resources configuration by writing configuration commands to the PCI controller. Because all PCI devices are in an inactive state upon system reset, they will have no addresses assigned to them by which the operating system or device drivers can communicate with them. Either the BIOS or the operating system `geographically addresses` the PCI devices (for example, the `first PCI slot`, `the second PCI slot`, `the third PCI slot`, or the integrated PCI devices, etc., on the motherboard) through the PCI controller using the per slot or per device IDSEL (Initialization Device Select) signals.

    - 참고 : https://en.wikipedia.org/wiki/PCI_configuration_space

     

    : 이렇게 PCI 디바이스에 주소가 할당되어 있으니, 이제 PCI 디바이스와 데이터 통신을 하고 싶다. `PCI Configuration Space` 공통 헤더를 이용하면 될까? 맞긴 맞다. 그러나, `PCI Configuration Space` 공통 헤더 정보는 모든 PCI 디바이스가 구현하고 있는 정보이기 때문에, 디테일하게 내가 `이 PCI 디바이스의 내부 레지스터를 바꾸고 싶어!` 와 같은 작업들은 `PCI Configuration Space` 공통 헤더 만으로는 부족하다. PCI 디바이스의 세부적인 정보를 알고 싶다면, 즉, 특정 PCI 디바이스의 내부 레지스터에 접근하고 싶다면, `Base Address(BAR)`에 접근해야 한다.

    PCI targets (except host bus bridges) are required to implement Base Address register(s) to request a range of addresses which can be used to provide access to internal registers or functions (refer to Chapter 6 for more details). The configuration software uses the Base Address register to determine how much space a device requires in a given address space and then assigns (if possible) where in that space the device will reside.

    - 참고 : PCI_Rev_30.pdf

    One of the most important functions for enabling superior configurability and ease of use is the ability to relocate PCI devices in the address spaces. At system power-up, device independent software must be able to determine what devices are present, build a consistent address map, and determine if a device has an expansion ROM. Each of these areas is covered in the following sections.

    - 참고 : PCI_Rev_30.pdf [ 6.2.5. Base Addresses ]

    Power-up software needs to build a consistent address map before booting the machine to an operating system. This means it has to determine how much memory is in the system, and how much address space the I/O controllers in the system require. After determining this information, power-up software can map the I/O controllers into reasonable locations and proceed with system boot. In order to do this mapping in a device independent manner, the base registers for this mapping are placed in the predefined header portion of Configuration Space.

    - 참고 : PCI_Rev_30.pdf [ 6.2.5.1. Address Maps ]

     

    : 맨 처음에 말했다. PCI의 주소 공간은 `데이터 공간`과 `설정 공간`으로 나뉜다고. 바로 이 BAR가 데이터 공간이다. 그런데 PCI 에서 데이터 공간은 RAM도 있고 I/O도 있다. 이걸 정하는 건 시스템 펌웨어가 정한다. 즉, BIOS 및 OS가 정하는 것이다. 우리는 시스템 펌웨어가 할다한 BAR를 통해 내가 원하는 PCI 디바이스와 통신을 하면 된다.

     

    : 그런데 문제가 있다. BAR는 I/O 주소 영역에 할당될 수 도 있고, RAM에 할당될 수 도 있다. 이걸 구분할 수 있어야 한다. 어떻게 구분할까? 아래 그림은 PCI BAR의 구조이다.

    : 0번째 비트가 어떤 값인지에 따라 RAM 인지 I/O 주소인지가 결정된다. 0번째 비트가 0 이면 RAM에 할당된 것이고, 1이면 I/O 에 할당된 것이다. 나머지 비트들은 딱히 중요하지 않다. 그리고 중요한 것은 위의 주소들이 정렬되어 있다는 것이다. RAM에 할당된 BAR같은 경우에 16바이트 정렬(0~3), I/O에 할당된 경우에 4바이트 정렬(0~1)이 된다.   

     

    - Capabilities List

    : `PCI Capabilities`는 PCI 디바이스에게 기본적으로 PCI 스펙에 정의된 기능외에 확장 및 추가 기능을 제공해준다고 보면 된다. `Capability Structure`는 맨 앞 8비트는 이 Capability가 어떤 기능을 하는지를 나타내는 ID 값을 명시한다. 그 다음 8비트는 다음 Capability의 주소를 적는다. 이렇게 여러 개의 Capability가 보여서 `Capabilities List`를 이루게 된다. 마지막, Capability는 다음 주소 포인터에 `0x00`을 넣는다.

    Certain capabilities added to PCI after the publication of revision 2.1 are supported by adding a set of registers to a linked list called the `Capabilities List`. This optional data structure is indicated in the `PCI Status Register` by setting the `Capabilities List bit` (bit 4) to indicate that the `Capabilities Pointer` is located at offset 34h. This register points to the first item in the list of capabilities.

    Each capability in the list consists of an `8-bit ID field` assigned by the PCI SIG, an 8 bit pointer in configuration space to the `next capability`, and some number of additional registers immediately following the pointer to implement that capability. Each capability must be `DWORD aligned`. The bottom two bits of all pointers (including the initial pointer at 34h) are reserved and must be implemented as 00b although software must mask them to allow for future uses of these bits. A pointer value of 00h is used to indicate the `last capability` in the list. Figure 6-10 shows how this list is constructed.

    Each defined capability must have a `SIG assigned ID code`. These codes are assigned and handled much like the Class Codes. Refer to Appendix H for a list of currently defined Capabilities. Each Capability must define the detailed register map for that capability. These registers must immediately follow the pointer to the next capability.

     

    " `Capabilities List`는 PCI Configuration Header 뒤에는 192B를 찾지한다. 흔히, `Device specific registers` 라고도 한다. 위에 Type 0과 Type 1 헤더를 보면 `capabilities pointer` 라는 필드가 있다. 이 필드는 PCI Configuration Header 뒤에 붙는 device specific registers들에 대한 오프셋을 의미한다. 일반적으로 capabilities pointer는 헤더가 처음으로 끝나는 0x40을 가리키는 경우가 일반적이다. 그러나 반드시 0x40을 가리킬 필요는 없다. capabilities pointer가 가리키는 device specific registers 들은 32-bit(4B)로 aligned 되어있어야 한다. `capabilities pointer`는 `Status Register`의 `Capabilities List(4번째 비트)` 비트가 SET인 경우에만 유효하다.

     

    : 좀 더 큰 범위로 보면 아래와 같다. 192B면 사실 많은 정보를 넣을 수 있는 구조는 아니다.

     

    - 예제 코드

    https://github.com/rajesh5310/SBUnix/blob/master/sys/ahci.c#L487
    uint64_t checkAllBuses(void) {
        int bus;
        int slot;
        unsigned short vendor,device;
        for(bus = 0; bus < 256; bus++) {
            for(slot = 0; slot < 32; slot++) {
                vendor = pciConfigReadWord(bus,slot,0,(0x00|0x0)); 
                device = pciConfigReadWord(bus,slot,0,(0x00|0x02));
                if(vendor==0x8086 && device==0x2922){
                    print("\nBUS[%d],DEVICE[%d],VENDOR[%d],DEVICE[%d]",bus,slot,vendor,device);
                    print("\nRead_Address Bar 5=[%x]",ReadWord(bus,slot,0,(0x24|0x0))); 
                    print("\nRead_Address=[%x]",ReadWord(bus,slot,0,(0x3c|0x0))& 0x000000000000ff00 ); 
                    return ReadWord(bus,slot,0,(0x24|0x0)); 
                    //mem_map_ahci((uint64_t)(&kernmem - &physbase + ((ReadWord(bus,slot,0,(0x24|0x0))) & 0xfffffffffffffff0)));
            //      mem_map_ahci(ReadWord(bus,slot,0,(0x24|0x0)));
                }
                //pciConfigReadWord (bus,device,0,0)
                // checkDevice(bus, device);
            }
        }
        return 0;
    }

    : 위의 예제에서 `pciConfigReadWord`에서 마지막 인자로 0x00, 0x02가 들어간다. configuration space의 0x00 offset(Vendor ID)과 0x02 offset(Device ID)의 값을 특정 디바이스가 어떤 디바이스인지 알 수 있다. 위 예제는 PCI 버스에서 AHCI Controller 디바이스를 탐색하는 코드이다.

     

    Configuration Common Header 출처 - PCI 3.0 revision specification

     


     

    : PCI 3.0 Revision Specification에 나오는 `DWORD`는 32-bit 데이터 타입을 의미한다.

    출처 - PCI 3.0 revision specification

     

     

    - Signal Definition

    : PCI 버스는 반드시 정의해야 하는 핀들(`Required`)과 옵션으로 정의한 핀들(`Optional`)로 나뉘어져 있다. 아래 그림에서는 왼쪽이 `Required` 핀들이고, 오른쪽이 `Optional` 핀들이다. 아래 네모박스에 `PCI Compiant Device`라고 작성되어 있는 것에 주목해야 하자.


    PCI_Rev_30.pdf

    : 대개 시스템 보드에 아예 납땜이 되어서 만들어지는 PCI 디바이스들은 당연히 왼쪽은 모두 구현되어야 한다. 그리고 시스템 또한 PCI Card, Connector로 동적으로 삽입될 수 있는 경우를 대비하여, 반드시 왼쪽 핀들에 대해 모두 지원을 해야한다.

     

    : 위에서 핀 네임 뒤에 `#`은 asserted state가 low voltage임을 의미한다. 접미사에 `#`이 없으면, asserted state가 high voltage임을 의미한다. 

     


    - Bus enumeration

    : wikipedia `PCI configuration space` 페이지에서 `Bus enumeration` 파트가 있다.

     

    : 모든 PCI 버스에 붙어있는 PCI 디바이스들은 시스템이 리셋되면 inactive 상태가 된다. 그러면, PCI 디바이스들은

     


    - BAR 사이즈

    : BAR 사이즈를 구하기 위해 우리가 알아야 하는 내용은 다음과 같다.

    1) BAR 사이즈는 Page 처럼 고정되는 사이즈다. 즉, 1KB, 2KB, 4KB 처럼 말이다.
    2) BAR는 I/O와 Memory로 구분될 수 있는데, 일단 최하위 4비트는 Read Only로 우리가 건드릴 수가 없다. 즉, 0xAAAA_AAAX 인 느낌이다. X는 알 수 없음을 의미한다.

    출처 -&nbsp;https://stackoverflow.com/questions/19006632/how-is-a-pci-pcie-bar-size-determined

     

     

    : BAR는 32-bit이므로, 여기에 0xFFFF_FFFF를 써보자. 즉, BAR 에다가 전부 1을 써보자. 그럼 BAR는 어떻게 될까? BAR는 고정되는 페이지라고 했다. 만약 BAR 사이즈가 4KB 라면, BAR를 다시 읽었을 때 0xFFFF_F00X이 되어야 한다. 왜? 저 값이 되는 이유는 BAR 사이즈가 4KB이고 4KB로 정렬되어야 하기 때문이다. 4KB는 2^12 이므로, 하위 12비트는 BAR의 오프셋으로 사용된다. 아래의 내용을 참고하자.

     

    출처 -&nbsp;https://stackoverflow.com/questions/19006632/how-is-a-pci-pcie-bar-size-determined

     

     

    - PCI Sideband Signal

    : 일반 `Signal`이 뭔지부터 알아보자. `신호`는 2가지 종류로 나눠볼 수 있다. 먼저, 물리적 `신호`가 있다. 이 신호는 실제 하드웨어적인 라인들을 의미한다. 예를 들어, 버스에 데이터 라인, 인터럽트 라인, 클락 라인, 컨트롤 라인 등등. 그런데, 논리적인 `신호`도 있다. 예를 들어, `프로토콜` 같은 것들이 논리적인 `신호`가 된다. 네트워크에서 HTTP 프로토콜에 따라 서로 패킷을 주고 받는데, 이 때 패킷들이 논리적인 `신호`가 될 수 있다.

     

    : `Sideband Signal`은 PCI 표준에서 조금 애매하게 정의한 감이 있다. `IRQ`, `hot-plug detection`, `power management signal` 등이 전부 사이드밴드 시그널인데, PCI 핀 그룹핑에서 `Optional Signal` 들에 있는 핀들이 거의 `Sideband signal`이라고 보면 된다.

    PCI provides all basic transfer mechanisms expected of a general purpose, multi-master I/O bus. However, it does not preclude the opportunity for product specific function/performance enhancements via sideband signals. A sideband signal is loosely defined as any signal not part of the PCI specification that connects two or more PCI compliant agents and has meaning only to these agents. Sideband signals are permitted for two or more devices to communicate some aspect of their device specific state in order to improve the overall effectiveness of PCI utilization or system operation.

    No pins are allowed in the PCI connector for sideband signals. Therefore, sideband signals must be limited to the system board environment. Furthermore, sideband signals may never violate the specified protocol on defined PCI signals or cause the specified protocol to be violated.
    ...

    The `Special Cycle` command provides a simple message broadcast mechanism on PCI. It is designed to be used as an alternative to physical signals when sideband communication is necessary. This mechanism is fully described in Section 3.6.2.
    ...

    The `Special Cycle` command provides a simple message broadcast mechanism on PCI. In addition to communicating processor status, it may also be used for logical sideband signaling between PCI agents, when such signaling does not require the precise timing or synchronization of physical signals. [ 3.6.2. Special Cycle ]

    - 참고 : PCI_Rev_30.pdf 

     

    - PCI 데이터 통신

    : PCI 3.0 Revision Specification에서 내가 생각하기에 가장 중요한 파트는 ` 3. Bus Operation` 이다.

     

     

     

    출처 - PCI 3.0 revision specification

    : 데이터 통신이 시작되면, PCI 버스에 붙어있는 디바이스들은 자신의 `Base Address register`를 비교해서 현재 데이터 통신의 발생이 자신 때문에 발생했는지 확인한다. 그리고 자신이 그 대상이 맞다면, `DEVSEL#` 라인을 low로 내린다.

    '공학 > 컴퓨터구조' 카테고리의 다른 글

    [컴퓨터 구조] AHCI  (0) 2023.08.10
    [컴퓨터 구조] ISA 버스  (0) 2023.08.09
    [컴퓨터 구조] PCI 인터럽트  (0) 2023.08.08
    [컴퓨터구조][ARM] ARMv7 부트 코드  (0) 2023.08.07
    [컴퓨터 구조] 디스크 주소 지정  (0) 2023.08.07
Designed by Tistory.