
SSD(Device)와 PC(Host)의 데이터 전송의 길인 버스(Bus)는 SSD의 속도과 용량 요구사항에 따라 PATA(IDE) → SATA → PCIe 순서로 발전되어 왔다. 현재 가장 일반적으로 사용되는 버스 표준은 PCIe이며 PCIe 이후로는 ‘새로운 버스 표준’은 나오지 않고, PCIe의 세대를 계속 발전시키는 추세이다. PCIe는 기본이 Lane 기반 구조라 Lane 수를 늘려서 대역폭을 자유롭게 확장 가능하며 이미 다양한 고성능 장치들과의 범용성이 좋아서 Gen3 → Gen4 → Gen5 → Gen6 로 계속 발전 중이다!
계속 새로운 버전이 나온다고 생각하면 됨!
1. 전송 속도(per lane) :: 한 레인(lane)당 보낼 수 있는 양(GT/s)
2. 대역폭 (x1, x4, x8, x16) :: 레인 수에 따라 총 데이터 처리량
3. 인코딩 방식 : 8b/10b, 128b/130b 등의 신호 인코딩
4. 지연시간(latency), 전력 효율
즉 PCIe Generation이 발전할수록 점점 고성능/저전력 고속 통신이 가능해진다!
| Generation | 전송 속도 (1 lane) | 인코딩 방식 | 실제 대역폭 (x1 기준) | 출시 시기 | 주요 특징 |
|---|---|---|---|---|---|
| Gen1 | 2.5 GT/s | 8b/10b | 250 MB/s | 2003 | 초기 버전, 인코딩 오버헤드 큼 |
| Gen2 | 5.0 GT/s | 8b/10b | 500 MB/s | 2007 | 속도 2배 증가 |
| Gen3 | 8.0 GT/s | 128b/130b | 약 985 MB/s | 2010 | 인코딩 효율 향상, 실효 대역폭 증가 |
| Gen4 | 16.0 GT/s | 128b/130b | 약 1.97 GB/s | 2017 | SSD 성능 급상승 시작 |
| Gen5 | 32.0 GT/s | 128b/130b | 약 3.94 GB/s | 2019~2021 | AI, 고성능 컴퓨팅용 서버에 주로 사용 |
| Gen6 | 64.0 GT/s (예정) | PAM-4 | 약 7.88 GB/s | 2023~2024 | PAM4 도입, ECC 추가, 고속 신호 전송 가능 |
✔️ 예시 : PCIe Gen3 , 8.0 GT/s , 128b/130b 인코딩 , x1 레인 기준
∴ Gen3 (1 lane)은 8.0 GT/s지만 실제 985 MB/s (약 1 GB/s) 가 전송됨 (다른 Gen들도 8배 정도의 차이로 생각하면 편함)
1. Detect State
2.Polling State
TS1 : 초기 신호 정렬 및 Receiver 감지TS2 : 속도/레인 수 협상 및 준비 상태 통지3.Configuration State
4.Recovery State
5.L0(Active)
PCIe 장치의 Gen (속도) 를 소프트웨어 또는 펌웨어/드라이버가 강제로 바꾸거나 확인하는데에 관련있는 핵심 레지스터는 아래와 같다.
| 레지스터 이름 | 역 |
|---|---|
| PCI_EXP_LNKCAP | 장치가 최대 지원하는 속도/레인 수 정보 제공 |
| PCI_EXP_LNKCTL2 | 목표 속도 (Target Link Speed) 설정 가능 |
| PCI_EXP_LNKSTA | 현재 속도 및 레인 수 (Current Link Status) 조회 |
| PCI_EXP_LNKSTA2 | 링크 오류 상태 등 추가 정보 제공 (선택 사항) |
이들은 모두 PCIe의 Configuration Space 안의 Capability 구조체 중 PCI Express Capability 영역에 위치해 있다.
▶ Configuration Space
[00h ~ FFh] : Standard PCI Header
[100h ~ xxx] : Extended Capabilities (ID 0x10 for PCIe Capability)
▶ Capability 구조체
Offset (from PCIe Cap Base):
- 0x0: PCI Express Capabilities Header
- 0x0C: Link Capabilities (LNKCAP)
- 0x10: Link Control / Status (LNKCTL / LNKSTA)
- 0x30: Link Control 2 / Status 2 (LNKCTL2 / LNKSTA2)
⭐ Target Link Speed 설정 : LNKCTL2 레지스터
1h : Gen1 (2.5 GT/s)
2h : Gen2 (5.0 GT/s)
3h : Gen3 (8.0 GT/s)
4h : Gen4 (16.0 GT/s)
5h : Gen5 (32.0 GT/s)
⭐ Link Training 재개 요청
Bit 5 : Retrain Link → 1로 설정하면 Link Training을 다시 시작
이 시점부터 LTSSM은 다시 Polling → Configuration → Recovery → L0 단계를 거치며 새로 설정된 Target Link Speed로 연결을 재시도
0. 준비 사항
sudo apt install pciutils
1. 장치의 최대 지원 속도 확인(LNKCAP)
sudo lspci -vvv -s <DEVICE_BDF>
▫️ 입력 예시
sudo lspci -vvv -s 0000:01:00.0
▫️ 출력 예시
LnkCap: Port #0, Speed 8GT/s, Width x4
LnkSta: Speed 5GT/s, Width x4
LnkCap은 장치의 최대 지원 속도, LnkSta는 현재 동작 속도
2. Target Link Speed 설정(LNKCTL2)
# 예: Gen2로 강제 설정 (value 2 = 0x2)
sudo setpci -s 0000:01:00.0 ECAP_PCI_EXP+30.w=0x2
▫️ECAP_PCI_EXP는 일반적으로 0x100 번지에 있으므로 아래와 같이 지정할 수도 있다.
sudo setpci -s 0000:01:00.0 100.w
sudo setpci -s 0000:01:00.0 130.w=0x0002 # LNKCTL2
3. Retrain Link 비트 설정(LNKCTL)
# Bit 5 (0x20)를 set → Link Retrain 요청
sudo setpci -s 0000:01:00.0 110.w=0x0020
해당 명령은 커널에서 관리 중인 PCIe 장치일 경우 바로 효과가 없을 수도 있으며, 이 경우 PERST# 리셋이나 unbind/rebind가 필요할 수 있다.
4. 변경 여부 확인
sudo lspci -vvv -s 0000:01:00.0 | grep LnkSta
▫️ 출력 예시
LnkSta: Speed 5GT/s (downgraded), Width x4
5. 참고 : 만약 Reset이 필요하면?
# Unbind 후 rebind 방법
echo 0000:01:00.0 | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver/unbind
echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/<driver_name>/bind
리눅스에서 PCI configuration space를 C++로 접근하는 방법은 아래와 같으며 3번째 방법이 안정적이고 범용적인 방법으로 알려져있다.
1. 준비 과정
sudo apt install libpci-dev
2. 예제 코드 (LNKCAP, LNKSTA, LNKCTL2 접근)
#include <pci/pci.h>
#include <cstdio>
int main() {
struct pci_access *pacc;
struct pci_dev *dev;
char namebuf[1024];
pacc = pci_alloc();
pci_init(pacc);
pci_scan_bus(pacc);
for (dev = pacc->devices; dev; dev = dev->next) {
pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_EXT_CAPS);
// 예: NVMe 장치만 필터링
if (dev->device_class == 0x010802) { // NVMe class code
printf("Device: %02x:%02x.%d\n", dev->bus, dev->dev, dev->func);
// PCIe Capability offset 추출
uint8_t pcie_cap = pci_find_cap(dev, PCI_CAP_ID_EXP);
if (!pcie_cap) continue;
// LNKCAP (max supported speed)
uint32_t lnkcap = pci_read_long(dev, pcie_cap + 0x0C);
uint32_t max_speed = lnkcap & 0xF;
printf("LNKCAP max speed: Gen%d\n", max_speed);
// LNKSTA (current speed)
uint16_t lnksta = pci_read_word(dev, pcie_cap + 0x12);
uint16_t cur_speed = lnksta & 0xF;
printf("LNKSTA current speed: Gen%d\n", cur_speed);
// LNKCTL2 (set target speed to Gen2)
pci_write_word(dev, pcie_cap + 0x30, 0x2);
printf("Target speed set to Gen2.\n");
// LNKCTL: Retrain bit set
uint16_t lnkctl = pci_read_word(dev, pcie_cap + 0x10);
lnkctl |= (1 << 5); // Set bit 5
pci_write_word(dev, pcie_cap + 0x10, lnkctl);
printf("Link retrain requested.\n");
}
}
pci_cleanup(pacc);
return 0;
}
3. 빌드
g++ -o pci_speed_setter your_code.cpp -lpci
이 코드는 루트 권한에서 실행되어야 하며, 일부 디바이스는 커널이 점유하고 있으면 효과가 없을 수 있다.