2026. 6. 17. 22:20ㆍ기초지식
Ordering Rules 개요
PCIe Ordering Rules는 여러 TLP가 동시에 전송될 때 수신 측에서 어떤 순서로 처리되어야 하는지를 정의한다. 잘못된 순서로 처리하면 데이터 일관성이 깨지거나 데드락이 발생한다.
PCIe는 전통적인 PCI의 Strong Ordering 모델을 기반으로 하면서, 성능 향상을 위한 옵션인 Relaxed Ordering(RO), ID-based Ordering(IDO), Snoop Not Required(SNR)를 추가로 정의한다.
- Ordering Rules는 단일 TC(Traffic Class) 내에서 적용됨
- 서로 다른 TC 간에는 Ordering 관계 없음
- Switch를 통과하더라도 동일 TC 내 Ordering은 유지되어야 함
- Ordering은 Peer-to-Peer 및 Root Complex 경유 트래픽 모두 적용
TLP 타입 분류 - Ordering 관점
Ordering 규칙은 TLP 타입을 크게 세 가지로 분류해서 적용한다.
Posted Request (P)
- Memory Write, Message - Completion 없이 전달 완료로 간주
- 전송 후 즉시 다음 작업 진행 가능
- PCIe에서 가장 강하게 순서 보장이 필요한 트랜잭션
Non-Posted Request (NP)
- Memory Read, IO Read/Write, Configuration Read/Write
- 반드시 Completion이 돌아와야 함
- Completion이 오기 전까지 해당 Address 범위 재접근 불가
Completion (Cpl)
- Non-Posted Request에 대한 응답
- Requester로 돌아가는 방향으로만 이동
- Split Transaction 모델: Completion은 별도 TLP로 전달
Default Ordering Rules
PCIe Spec Table 2-34에 정의된 Default Ordering Rules이다. 각 셀은 Row(먼저 도착한 TLP)가 Column(나중에 도착한 TLP) 보다 먼저 처리되어야 하는지 여부를 나타낸다.
Pass 규칙 표현:
- Yes: Row가 Column을 추월하는 것 허용 (먼저 처리 가능)
- No: Row가 Column을 추월하는 것 금지 (순서 유지 필수)
- Yes*: 특정 조건에서만 허용
P NP CplD CplX
P --> No Yes Yes Yes
NP --> No No Yes Yes
CplD --> No No No No
CplX --> No No No No
Rule 1 - Posted vs Posted: No Pass
Posted Request는 다른 Posted Request를 추월하지 못한다. Memory Write 순서가 Producer-Consumer 관계에서 반드시 보장되어야 하기 때문이다.
- 예: Write A → Write B 순서로 전송 시, 수신 측도 A→B 순서로 처리 보장
- 위반 시 Data Corruption 발생 가능
Rule 2 - Non-Posted vs Posted: No Pass
Non-Posted Request는 Posted Request를 추월하지 못한다. 이미 전송된 Posted Write가 Non-Posted Read보다 먼저 메모리에 반영되어야 Read 결과가 정확하다.
- 예: Write X → Read X 순서 시, Read가 Write보다 먼저 도달하면 오래된 값 반환
- Producer가 데이터를 쓴 후 Flag를 세팅하는 패턴에서 필수
Rule 3 - Completion vs 모든 TLP: No Pass
Completion은 어떤 TLP도 추월하지 못하고, 반대로 어떤 TLP도 Completion을 추월하지 못한다. 데드락 방지가 핵심 이유이다.
- Completion이 Non-Posted Request를 추월하면 Request에 대한 응답이 요청보다 먼저 도착하는 상황 발생
- Deadlock 발생 시나리오 방지를 위해 강제
Rule 4 - Posted는 Non-Posted, Completion을 추월 가능
Posted Request는 Non-Posted Request와 Completion보다 먼저 처리될 수 있다. 이는 Buffer Full로 인해 Non-Posted가 Block된 상황에서 Posted가 막히는 것을 방지한다.
- 이 규칙이 없으면 Non-Posted Buffer Full 시 Posted까지 멈추는 데드락 발생
- 단, 같은 Address 공간에 대한 충돌은 별도 고려 필요
Producer-Consumer Ordering Model
PCIe Ordering의 핵심 사용 패턴이다. 공유 메모리를 통해 두 디바이스가 통신할 때 아래 순서가 반드시 보장되어야 한다.
[Producer 측 순서]
1. Payload Data를 Shared Memory에 Memory Write (Posted)
2. Flag/Descriptor를 Flag Memory에 Memory Write (Posted)
[Consumer 측 순서]
1. Flag Memory Read - Flag 확인 (Non-Posted)
2. Payload Data Memory Read - 실제 데이터 읽기 (Non-Posted)
[보장 필요 조건]
- Step 1의 Write가 Step 2의 Write보다 먼저 메모리에 반영되어야 함
-> Posted는 Posted를 추월 불가 (Rule 1)
- Consumer의 Flag Read가 Producer의 Flag Write보다 먼저 도달하면 안 됨
-> NP는 P를 추월 불가 (Rule 2)
이 순서가 깨지면 Consumer가 Flag를 보고 데이터를 읽었을 때 아직 데이터가 메모리에 없는 상황이 발생한다.
Relaxed Ordering (RO)
Relaxed Ordering은 TLP Header의 Attr[1] 비트로 설정한다. RO=1로 설정된 TLP는 Default Ordering Rules 중 일부를 완화해서 성능을 높인다.
RO 허용 시 변화
- RO=1인 Posted Request는 다른 Posted Request를 추월 가능
- RO=1인 Posted Request는 Non-Posted Request를 추월 가능
- RO=1인 Completion은 다른 Completion을 추월 가능
RO 사용 조건
- 순서에 무관한 데이터 전송에만 사용 가능
- Producer-Consumer 패턴, DMA Descriptor 기반 전송에는 사용 금지
- Requester가 RO=1을 설정할 책임이 있음 - 잘못 설정 시 데이터 일관성 문제 발생
- Switch는 RO 비트를 보고 내부 버퍼에서 재정렬 허용 여부를 판단
RO 관련 TLP Header 비트
TLP Header DW0:
[23:22] Attr[1:0]
- Attr[1]: Relaxed Ordering (RO)
0 = Default Ordering (강한 순서 보장)
1 = Relaxed Ordering (순서 완화)
- Attr[0]: No Snoop (NS)
0 = 캐시 일관성 유지 필요
1 = 스누핑 불필요
PCIe 2.0 이후 추가된 Attr[2] (IDO):
- Attr[2]: ID-based Ordering
ID-based Ordering (IDO)
IDO는 PCIe 2.1부터 추가된 기능이다. Requester ID(Bus:Dev:Func)가 다른 TLP끼리는 Ordering 관계가 없다고 선언하는 방식이다.
동작 원리
- IDO=1로 설정된 TLP는 동일 Requester ID를 가진 TLP끼리만 Ordering 적용
- 서로 다른 Requester ID를 가진 TLP들은 독립적으로 처리 가능
- Root Complex에서 다수의 CPU가 각자 독립적으로 PCIe 트래픽을 발생시킬 때 유용
IDO 사용 조건
- Device Control 2 Register의 IDO Request Enable 비트를 설정해야 사용 가능
- Requester가 IDO Request를 보낼 때 Attr[2]=1 설정
- Completer가 IDO Completion을 보낼 때 별도 IDO Completion Enable 비트 필요
- Switch는 IDO 비트를 기준으로 서로 다른 Requester ID의 트래픽을 독립 처리 가능
IDO 효과
[IDO 미사용 - Default]
CPU0 Write(A) -> CPU1 Read(B) -> CPU0 Write(C)
Switch는 이 세 TLP를 순서대로 처리해야 함
[IDO 사용]
CPU0 Write(A) [IDO=1, ReqID=CPU0]
CPU1 Read(B) [IDO=1, ReqID=CPU1]
CPU0 Write(C) [IDO=1, ReqID=CPU0]
Switch는 CPU0 트래픽끼리만 순서 보장
CPU1 트래픽은 CPU0 트래픽과 독립적으로 처리 가능
-> 전체 처리량(throughput) 향상
No Snoop (NS)
No Snoop은 TLP Header Attr[0] 비트로 설정한다. NS=1로 설정하면 이 TLP가 접근하는 데이터는 CPU 캐시에 없으므로 Cache Snoop이 불필요하다고 Root Complex에 알린다.
- Root Complex는 NS=1인 TLP를 처리할 때 CPU Cache Invalidation/Snoop 생략 가능
- DMA 버퍼처럼 CPU가 직접 접근하지 않는 영역에 적합
- NS=1이지만 실제로 CPU 캐시에 데이터가 있으면 데이터 일관성 문제 발생
- Ordering 자체와는 무관하나 함께 사용되는 경우 많음
Snoop Not Required (SNR)
SNR은 PCIe 4.0에서 추가된 기능이다. Attr[3] 비트를 사용한다. No Snoop과 유사하지만 더 명확하게 스누핑이 필요 없는 경우를 지정한다.
- Attr[3]=1: 이 TLP는 Snoop이 전혀 필요 없음을 명시
- 주로 AI/ML 가속기의 대용량 텐서 데이터 전송에서 활용
- Root Complex가 Snoop 처리를 완전히 생략할 수 있어 레이턴시 감소
- CXL(Compute Express Link) 환경에서도 활용
Ordering Rules와 Deadlock
Ordering Rules를 잘못 설계하면 Deadlock이 발생한다. PCIe는 이를 방지하기 위해 특정 Ordering 조합을 명시적으로 금지한다.
Deadlock 시나리오 예시
[시나리오: Completion이 Non-Posted를 기다리는 경우]
Device A ---[NP Read]--> Switch --> Device B
Device B ---[Cpl]------> Switch --> Device A
Switch 내부 NP Buffer가 꽉 찼을 때:
- Completion은 NP Buffer에 들어있는 Read Request 완료를 기다림
- NP Buffer의 Read Request는 Completion Buffer의 공간을 기다림
(Completion이 먼저 지나가야 공간이 생김)
- 상호 대기 -> Deadlock
해결: Switch는 Completion이 NP를 추월하도록 설계
(충분한 Completion Buffer 보장)
Deadlock 방지 설계 원칙
- Completion Buffer는 항상 충분한 크기 확보 필수
- Posted Buffer가 꽉 차도 Non-Posted 처리를 막지 않도록 분리
- Switch 설계 시 각 TLP 타입별 독립 Buffer 운영
- Flow Control Credit으로 Buffer Full 상황 사전 방지
Ordering Rules 종합 요약
Default Ordering Rules 요약:
1. P(Posted)는 P 추월 불가 - Memory Write 순서 보장
2. NP는 P 추월 불가 - Read가 Write보다 먼저 도달 방지
3. Cpl은 어떤 것도 추월 불가 - Deadlock 방지
4. P는 NP, Cpl 추월 가능 - Buffer Full Deadlock 방지
완화 옵션:
- RO=1: P끼리 추월 허용, Cpl끼리 추월 허용 (순서 무관 데이터에 한함)
- IDO=1: 다른 Requester ID 간 Ordering 관계 제거 (다중 CPU/Function 환경)
- NS=1: Cache Snoop 생략 요청 (Ordering과 무관)
- SNR=1: Snoop 완전 불필요 선언 (PCIe 4.0+)
선택 기준:
- 데이터 의존성 있음 -> Default Ordering 유지
- 독립적 스트림 데이터 -> RO=1 고려
- 다중 Function/CPU -> IDO=1 고려
- DMA 전용 버퍼 -> NS=1 또는 SNR=1 고려