이 글은 제가 PCIe를 공부하면서 겪은 시행착오를 바탕으로 정리한 글입니다. PCIe를 처음 접하는 분들에게 좋은 길라잡이가 되었으면 합니다.
이전 글)
Data Link Layer Overview
PCI Express (PCIe)의 데이터 링크 계층은 전송 데이터의 무결성을 보장하고 효율적인 데이터 흐름 제어를 제공합니다. 이 계층은 CRC와 LCRC를 활용하여 데이터 에러를 검출하고 수정하며, 크레딧 기반의 흐름 제어 시스템을 통해 네트워크 혼잡을 방지하고 데이터 전송의 효율성을 높입니다. 또한, ACK와 NACK 시그널을 사용하여 패킷의 수신 성공 여부를 송신자에게 통보하고, 필요시에는 데이터 패킷의 재전송을 관리합니다.
DLLP
DLLP(Data Link Layer Packet)은 특히 data link layer에서 사용되는 작은 크기를 가지는 packet 유형입니다. DLLP는 큰 data block을 전송하는 TLP와는 달리, link의 상태 관리, 에러 및 flow control 업데이트와 같은 작업을 수행하기 위해 설계되었습니다. 다음 예시 중 왼쪽은 DLLP의 공통부분에 대한 구조를 나타내고 있으며, 오른쪽은 Ack/Nak 관련 DLLP의 경우에 대한 packet 구조를 나타내고 있습니다.
Ack/Nak Protocol
Ack/Nak 프로토콜은 데이터 통신에서 신뢰성을 보장하기 위해 널리 사용되는 메커니즘입니다. 이 프로토콜은 송신자가 데이터를 보낸 후, 수신자로부터 데이터 수신에 대한 Ack 혹은 Nak 응답을 받아 통신의 신뢰성을 높입니다.
Ack/Nak protocol의 기본 원리
- 송신자가 데이터 패킷을 전송합니다.
- 수신자는 패킷을 받고 검증합니다.
- 데이터 패킷이 올바르게 받아지고 에러가 없으면, 수신자는 "Ack" 신호를 송신자에게 보냅니다.
- 데이터에 오류가 있거나 패킷이 손상된 경우 "Nak" 신호를 송신자에게 보내 요청한 데이터의 재전송을 요구합니다.
- 송신자는 수신자의 응답을 기다립니다.
- Ack를 받으면, 다음 데이터 패킷을 전송합니다.
- Nak을 받으면, 에러가 발생한 데이터 패킷을 다시 전송합니다.
PCIe에서의 Ack/Nak protocol
- DLLP: PCIe는 ack와 nak 정보를 전송하기 위해 특별한 유형의 패킷인 DLLP를 사용합니다. 이 패킷들은 데이터 패킷의 수신 성공 여부를 송신자에게 알리는 데 사용됩니다.
- 에러 복구: Nak을 받은 경우, 송신자는 해당 데이터를 재전송하여 에러로 인한 데이터 손실을 방지합니다.
다음 그림은 PCIe의 data link layer에 구현된 ack/nak protocol 모습입니다. 자세히 살펴보면, transaction 계층에서 생성된 TLP가 data link 계층으로 넘어온 다음, sequence number와 LCRC가 양옆에 추가됩니다. 이제 TLP는 다른 장치에서 안전하게 도착했다는 확인(Ack)를 받을 때까지 retry buffer에 복사본을 저장합니다. 이는 전송 중 발생할 수 있는 문제로부터 패킷을 보호하고, 필요한 경우 재전송을 하기 위함입니다. 저장된 TLP는 PHY로 전달되어 다른 장치로 전송됩니다. 다른 장치에서 TLP의 sequence number 및 CRC check를 통한 Ack/Nak 응답을 송신 측으로 다시 전달합니다. 이 응답에 따라 송신 측의 TLP는 retry buffer에서 제거될 수 있으며, Nak을 받은 경우, buffer에 있는 TLP들을 다시 전송해야 합니다.
Elements of the Ack/Nak Protocol
Transmitter Elements
Ack/Nak Protocol을 구현할때 송신 측에 어떠한 일들일 벌어지는지 자세히 알아봅시다. 먼저 TLP가 transaction layer로부터 들어보니다. 만약 replay 중이라면 들어오는 TLP는 모두 blocking 됩니다. replay 중이 아닌 상황에서 TLP가 정상적으로 data link layer 들어왔다면, 제일 먼저 NEXT_TRANSMIT_SEQ(NTS) 값으로 sequence number를 할당합니다. 그 뒤, NTS값을 1 증가시킵니다. 이제 sequence number가 추가된 TLP에 대한 LCRC를 생성합니다. LCRC Generator를 통해 32bit LCRC가 생성되어 TLP의 끝에 추가됩니다. LCRC까지 추가된 TLP를 retry buffer에 복사 저장합니다. 이제 송신 측은 수신 측으로 패킷을 전송하기 위한 모든 일련의 작업을 마무리했습니다.
Receiver Elements
Ack/Nak Protocol을 구현하기 위해 수신측에서 어떠한 일들이 벌어지는지 알아봅시다. 먼저 Tx로부터 받은 TLP를 CRC checking을 합니다. 만약 CRC check에서 실패한다면 해당 TLP를 버리고, NAK_SCHEDULED 플래그를 띄웁니다. 그다음 expected sequence number를 뜻하는 NEXT_RCV_SEQ(NRS)에서 1을 뺀 값을 sequence number로 DLLP에 할당합니다. 즉, 마지막으로 Ack 된 sequence number를 의미합니다. 예를 들어 sequence number 10번을 기다리고 있었는데 에러가 났다면, 마지막으로 Ack 된 sequence number는 9번이 될 것입니다. 참고로 전달받은 TLP에도 sequence number가 있는데 이를 사용하지 않는 이유는 CRC 에러가 발생했기 때문에 전달받은 TLP는 더 이상 신뢰할 수 없기 때문입니다.
자 이제 CRC 체크를 통과했다고 가정해봅시다. 이제부터는 sequence number를 확인해야 합니다. 들어온 TLP의 sequence number와 예상되는 sequence number인 NRS값을 비교해야 합니다. 여기서 3가지 상황으로 나누어 설명하겠습니다.
- Seq Num == NRS
- 이 경우는 data link 관점에서 패킷이 정상이라는 의미입니다. 이 패킷은 이제 sequence number와 LCRC가 제거되고 transaction layer로 올라갈 것입니다. 이와 별개로 data link의 NRS 값을 증가시킵니다. 이는 다음에 받을 패킷의 sequence number를 추적하기 위함입니다. 또한 Nak flag를 초기화시킵니다. 그리고 Ack DLLP를 스케줄링하며, 설정된 AckNak Latency Timer가 만료되었을 때, 스케줄 된 Ack DLLP가 전송됩니다.
- Seq Num < NRS
- NRS가 들어온 TLP의 sequence number보다 크다는 것은 동일한 TLP가 여러번 들어온 것입니다. 즉, 송신 측에서 스스로 재전송한 것인데, 수신 측에서 Nak를 받은 것은 아니지만 수신 측에서 오는 Ack/Nak를 정해진 시간 내에 받지 못해서 송신 측이 스스로 replay 한 경우입니다.
- 수신 측에서는 중복된 TLP는 필요하지 않으므로 폐기합니다. 또한 NRS는 증가하지 않습니다. 다만 last good TLP (NRS-1) 값을 sequence number로 할당한 Ack 유형의 DLLP를 송신 측에 전송합니다. 마찬가지로 AckNak Latency timer가 만료 시 스케줄 된 Ack DLLP가 전송됩니다.
- Seq Num > NRS
- 이 경우는 packet이 중간에 손실된 상황입니다. 마찬가지로 수신측은 들어온 TLP를 폐기하며, NRS값 역시 증가하지 않습니다. 다만 NAK_SCHEDULED flag를 설정하여 Nak DLLP를 전송합니다. 이 때는 AckNak Latency Timer를 기다릴 필요 없이 즉각적으로 전송됩니다.
- 여기서 주의 할 것이 Nak DLLP도 last good TLP의 sequence number를 담고 있습니다. Nak 신호라고 해서 아무런 의미가 없는 것은 아닙니다.
Transmitter Elements for Incoming DLLPs
다시 송신측으로 돌아와서 수신 측에서 Ack/Nak DLLP를 받았을 때 송신 측의 동작을 자세히 살펴보겠습니다. 먼저 수신 측으로 들어온 DLLP를 CRC 체크를 합니다. 만약 CRC 에러가 발생했다면 전달받은 DLLP를 폐기합니다. CRC 체크에서 아무런 문제가 없다면 이제는 AckD_SEQ(AS)와 DLLP의 sequence number를 비교합니다. AS에는 가장 최근에 수신한 Ack 또는 Nak의 sequence number가 저장되어 있습니다. 만약 AS값과 전달받은 DLLP의 seq num이 다르다면 즉, 더 AS보다 더 크다면 transaction이 전진(progress)하고 있다는 것을 의미합니다. 그럼 Ack/Nak를 기준으로 retry buffer에서 제거할 수 있는 TLP를 제거합니다.
송신측이 TLP를 수신 측으로 전송할 때, TLP의 마지막 symbol이 전송되고 나면 REPLAY_TIMER가 시작됩니다. 그리고 Ack/Nak DLLP가 수신되어 AS값이 업데이트되면, 이는 전송의 전진(progress)을 의미하며, REPLAY_TIMER를 다시 reset 합니다. retry buffer에 TLP가 남아있을 때까지 이러한 반복은 계속되며, retry buffer에 TLP가 없다면 더 이상 Timer는 동작하지 않습니다. 왜냐하면 모든 패킷이 성공적으로 처리되었으므로 더 이상 타이머가 필요하지 않기 때문입니다.
하지만 어떠한 이유로 예를 들어 Ack/Nak가 버려졌거나 늦게온다면, REPLAY_TIMER가 reset 되지 않을 것이며 결국 TIMEOUT이 발생할 것입니다. TIMEOUT이 발생한다면 retry buffer에 있는 모드 packet들이 재전송됩니다.
만약 AS값이 DLLP의 sequence num와 같다고 해봅시다. 즉, progress가 진행되지 않고 있지 않고, 그 DLLP가 Nak이라면 retry buffer의 모든 TLP가 마찬가지로 재전송되며, REPLAY_NUM을 1 증가시킵니다. REPLAY_NUM은 Nak 또는 REPLAY_TIMER의 TIMEOUT 횟수를 추적하는 2bit 카운터입니다. REPLAY_NUM 카운터가 11b에서 00b로 넘어가면(즉, 동일한 TLP 전송 시도가 4번 실패했음을 의미), data link layer는 자동으로 physical layer에 link를 retraining 하도록 강제합니다. (LTSSM이 recovery 상태로 전환)
Ack/Nak Examples
Ack Example
다음 예시는 Ack 신호를 주고 받는 예시를 나타낸 모습입니다.
Nak Example
다음 예시는 Nak가 발생했을 때, 수신 및 송신 측에서 일어나는 일들을 모사한 모습입니다.
Next Topic)
이번 글을 통해서 PCIe의 data link layer가 하는 역할에 대해 자세히 알아봤습니다. CXL에서도 Ack/Nak 개념이 마찬가지로 존재하기 때문에 이번 내용을 이해했다면 CXL의 retry 메커니즘도 쉽게 이해할 수 있을 것입니다. 이벌 글로 data link layer에 대한 설명은 마무리 하며, 다음 글부터는 PCIe의 physical layer 중 logical 부분을 자세히 다뤄보겠습니다.
Reference)
- PCI Express® Base Specification Revision 5.0 Version 1.0
- PCI Express Technology 3.0 (minshare)
'Interface Standards > PCIe' 카테고리의 다른 글
[7] PCIe - Physical Layer (PCIe Gen1 & Gen2) - Receive Logic (0) | 2024.05.26 |
---|---|
[6] PCIe - Physical Layer (PCIe Gen1 & Gen2) - Transmit Logic (0) | 2024.05.26 |
[4] PCIe - Transaction Layer (Ordering) (0) | 2024.05.12 |
[3] PCIe - Transaction Layer (Quality of Service & Flow Control) (0) | 2024.05.12 |
[2] PCIe - Transaction Layer (TLPs) (0) | 2024.05.12 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!