---
title: "같은 ID를 쓰는 두 요청자에게 AXI 응답을 돌려주는 법"
author: "브레이브브라우저쓰자"
published_at: "2026-10-27T22:24:04.106Z"
updated_at: "2026-10-06T02:43:43.363Z"
tags: ["axi crossbar"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%EA%B0%99%EC%9D%80-id%EB%A5%BC-%EC%93%B0%EB%8A%94-%EB%91%90-%EC%9A%94%EC%B2%AD%EC%9E%90%EC%97%90%EA%B2%8C-axi-%EC%9D%91%EB%8B%B5%EC%9D%84-%EB%8F%8C%EB%A0%A4%EC%A3%BC%EB%8A%94-%EB%B2%95"
ai_assisted: true
---

# 같은 ID를 쓰는 두 요청자에게 AXI 응답을 돌려주는 법

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/%EA%B0%99%EC%9D%80-id%EB%A5%BC-%EC%93%B0%EB%8A%94-%EB%91%90-%EC%9A%94%EC%B2%AD%EC%9E%90%EC%97%90%EA%B2%8C-axi-%EC%9D%91%EB%8B%B5%EC%9D%84-%EB%8F%8C%EB%A0%A4%EC%A3%BC%EB%8A%94-%EB%B2%95)

두 요청자가 같은 메모리에 읽기를 보내고 둘 다 ID 2를 사용했다. 메모리는 응답에 ID 2를 붙여 돌려주지만, 어느 요청자에게 보내야 할까? AXI crossbar는 주소로 목적지를 찾는 회로인 동시에, 응답이 원래 요청자로 돌아갈 수 있도록 정보를 보존하는 회로다. 주소 decode만 맞아서는 이 두 번째 역할이 해결되지 않는다.

crossbar의 입구에는 요청을 보내는 여러 주체가 있고 출구에는 메모리와 주변장치가 있다. 여러 입구가 한 출구를 동시에 원하면 중재가 필요하다. 요청을 받은 다음 응답이 오기까지 시간이 걸리므로, 현재 중재에서 선택된 입구가 이전 응답의 주인이라고 가정할 수는 없다.

PULP의 AXI crossbar는 입구 수에 맞춘 비트를 거래 ID에 추가해 응답의 경로를 구분한다. 예를 들어 입구가 두 개면 구분 비트 하나가 필요하고, 원래 ID가 3비트라면 출구 쪽 ID는 4비트가 된다. 실제 mux 구현은 추가 비트에서 B와 R 응답의 전달 대상을 선택한다. [PULP axi_xbar, Design Overview](https://github.com/pulp-platform/axi/blob/master/doc/axi_xbar.md), [PULP axi_mux의 ID 확장·B/R 경로](https://github.com/pulp-platform/axi/blob/master/src/axi_mux.sv)

이를 가상의 두 요청자 M0, M1에 적용해 보자. 둘 다 로컬 ID 2, 즉 0b010을 사용한다. 출구로 보낼 ID를 `{입구번호, 로컬ID}`로 정하면 M0의 요청은 0b0010, M1의 요청은 0b1010이다. 메모리에서 0b1010이 먼저 돌아오면 M1에게 전달하고, 높은 비트를 제거한 로컬 ID 2를 보여 준다. 나중에 0b0010이 돌아오면 M0에게 로컬 ID 2로 전달한다.

응답이 반대 순서로 와도 대응은 달라지지 않는다. 두 출구 ID가 서로 다르기 때문이다. 반면 구분 비트를 붙이지 않고 ID 2만 내보냈다면, 기억한 별도 순서나 라우팅 상태 없이 응답의 주인을 알 수 없다. 넓혀야 하는 비트를 실수로 잘라 두 거래가 다시 같은 ID로 보이게 만들어도 같은 문제가 생긴다. ID 폭은 단순한 배선 폭 설정이 아니라 응답 경로 정보의 용량이다.

이 방식에서도 요청자의 원래 ID 안의 순서는 보존해야 한다. M0가 같은 ID로 연속 보낸 두 읽기의 순서를 M1의 응답과 섞을 수 있는 문제와, M0 내부에서 뒤집을 수 있는 문제는 다르다. 추가 비트는 서로 다른 요청자의 이름 공간을 분리한다. 원래 같은 ID에 적용되던 순서 요구를 지워 주지는 않는다.

쓰기 데이터에는 더 직접적인 상태가 필요하다. 일반 AXI4의 W에는 ID가 없으므로, AW에서 정한 연결 순서를 따라 W를 보내야 한다. 가상으로 같은 출구에 M0의 두 beat짜리 쓰기 주소가 먼저 선택되고 M1의 한 beat짜리 쓰기 주소가 나중에 선택되었다고 하자. W 경로가 따라야 할 입구 순서는 [M0,M1]이다.

```text
AW의 연결 결정: M0 → M1
W의 전달: M0 beat0 → M0 beat1(last) → M1 beat0(last)
```

M1 데이터가 먼저 준비되었다고 M0의 두 beat 사이에 끼워 넣으면 데이터가 잘못된 주소 거래에 붙는다. PULP mux는 AW의 연결 결정을 W용 큐에 기억하고, 그 순서로 데이터 경로를 선택한다. 마지막 W beat가 수락될 때 해당 연결 항목을 끝낸다. 여기서 큐에 기록하는 내부 시점은 구현의 중재·저장 제어에 맞춰 정해지며, 단순히 매 사이클의 주소 포트를 다시 decode하는 것으로 대체할 수 없다. [PULP axi_mux, W FIFO·W Channel](https://github.com/pulp-platform/axi/blob/master/src/axi_mux.sv)

응답 상태 역시 valid가 보였다는 이유만으로 끝내면 안 된다. 원래 요청자가 BREADY나 RREADY를 내리면 응답이 아직 수락되지 않은 상태다. 경로 정보와 응답을 유지해야 한다. 읽기 burst는 마지막 beat까지 추적해야 하므로 첫 데이터 뒤에 슬롯을 재사용하면 남은 beat가 다른 거래에 연결될 수 있다.

주소 쪽에서도 범위를 정확히 정의해야 한다. PULP crossbar의 범위는 시작 주소를 포함하고 끝 주소를 제외한다. 가상 범위 [0x1000,0x2000)와 [0x2000,0x3000)를 두면 0x2000은 둘째 목적지다. 양쪽 검사에 끝점을 포함하면 경계 주소에서 두 목적지가 동시에 선택될 수 있다. decode 실패에 대한 응답 경로까지 갖추어야 요청이 조용히 사라지는 상황을 막을 수 있다.

응답 재정렬 공간이 없는 구현은 같은 ID로 서로 다른 목적지에 요청할 때 앞 거래의 완료를 기다리는 등 보수적인 방법으로 순서를 지킬 수 있다. 실제 PULP crossbar도 이 경우의 stall을 설명한다. 멈춘 주소 채널이 반드시 중재 실패를 의미하지는 않는다. 순서를 지키기 위한 설계 선택일 수 있다.

AXI crossbar를 검토할 때 요청 주소가 올바른 출구로 갔는지 확인한 다음, 그 요청의 주인을 가리키는 정보가 언제까지 살아 있는지 추적해 보자. 주소 선택, W 데이터의 순서, 응답 경로가 서로 맞아야 한 거래가 완성된다. 좋은 라우팅은 첫 목적지를 찾는 것과 응답을 끝까지 되돌려 보내는 것을 함께 해결한다.

