---
title: "포트는 맞는데 동작은 다른 하드웨어 인터페이스 계약"
author: "브레이브브라우저쓰자"
published_at: "2026-10-07T06:08:04.165Z"
updated_at: "2026-10-05T13:50:23.521Z"
tags: ["하드웨어 인터페이스 계약"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%ED%8F%AC%ED%8A%B8%EB%8A%94-%EB%A7%9E%EB%8A%94%EB%8D%B0-%EB%8F%99%EC%9E%91%EC%9D%80-%EB%8B%A4%EB%A5%B8-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B3%84%EC%95%BD"
ai_assisted: true
---

# 포트는 맞는데 동작은 다른 하드웨어 인터페이스 계약

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

[원문](https://ariser.io/@writer_a9817d5a8de0/%ED%8F%AC%ED%8A%B8%EB%8A%94-%EB%A7%9E%EB%8A%94%EB%8D%B0-%EB%8F%99%EC%9E%91%EC%9D%80-%EB%8B%A4%EB%A5%B8-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B3%84%EC%95%BD)

두 모듈의 포트 이름과 비트폭이 모두 일치한다. 연결 코드도 컴파일된다. 그런데 한 모듈은 stall 동안 데이터를 바꾸고, 다른 모듈은 처음 제시한 데이터를 유지한다고 믿는다. 오류는 타입 검사 밖에 있다. **하드웨어 인터페이스 계약은 신호를 어떻게 연결하는가뿐 아니라, 어느 시점에 어떤 의미를 갖는가까지 포함한다.**

공통 인터페이스를 잘 만드는 목적은 타이핑을 줄이는 데서 끝나지 않는다. 생산자와 소비자가 같은 거래를 같은 사건으로 인식하도록 만드는 데 있다. 구조를 공유했는데 행동 규칙이 서로 다르면, 재사용이 버그를 넓히는 통로가 될 수 있다.

## 구조가 맞는 연결부터 시작하기

8비트 데이터와 두 제어 신호를 묶는 설명용 인터페이스를 생각하자.

```systemverilog
interface byte_link;
  logic       valid;
  logic       ready;
  logic [7:0] data;

  modport producer(output valid, data, input ready);
  modport consumer(input valid, data, output ready);
endinterface
```

modport는 각 모듈이 어느 신호를 읽고 쓰는지 드러낸다. 생산자는 valid와 data를 만들고 소비자는 ready를 만든다. 잘못된 방향 연결이나 폭을 확인할 단서가 된다. 그러나 이 선언에는 거래를 클록 상승 에지에서 세는지, valid가 높을 때 data를 유지하는지, reset 중 ready의 의미가 무엇인지 들어 있지 않다.

Chisel의 Bundle도 관련 신호를 한 구조로 묶는 포트 타입이다. Flipped는 그 안의 방향을 뒤집어 상대편 포트를 만드는 데 쓰인다. [Chisel의 인터페이스 문서](https://www.chisel-lang.org/docs/explanations/interfaces-and-connections)는 구조적 묶음과 방향 연결을 설명한다. 이런 타입 정보는 유용하지만 그 자체가 통신 규칙을 완성하지는 않는다.

## 같은 파형을 서로 다르게 읽는 두 모듈

단일 클록의 상승 에지에서 valid와 ready가 모두 1이면 데이터를 받는다고 정하자. 다음은 계약 차이를 드러내기 위한 가상 추적이다. 값은 각 에지 직전의 상태다.

| 에지 | valid | ready | data |
| --- | --- | --- | --- |
| 0 | 1 | 0 | 0x2A |
| 1 | 1 | 0 | 0x2B |
| 2 | 1 | 1 | 0x2C |

에지 2에 수락되는 데이터는 0x2C다. 하지만 생산자가 첫 제시 이후 수락까지 data를 유지해야 한다는 규칙이라면, 이미 에지 1에서 위반했다. 반대로 data는 수락 에지에만 의미 있고 기다리는 동안 바꿔도 된다는 약한 규칙이라면, 이 파형은 허용될 수 있다.

어느 규칙을 선택할지는 인터페이스의 목적에 달려 있다. 중요한 것은 소비자가 규칙을 추측하지 않는 것이다. 예를 들어 소비자가 에지 0에 data를 미리 저장해 두고 에지 2에 거래가 성립했다고 처리한다면, 약한 규칙의 생산자와 연결했을 때 0x2A를 처리한다. 신호 타입은 맞지만 거래의 데이터가 서로 다르다.

Chisel 문서는 DecoupledIO의 관례가 valid나 ready의 해제, bits의 안정성을 보장하지 않는다고 설명한다. 반면 IrrevocableIO에는 valid를 제시한 뒤 ready를 기다리는 동안 bits를 유지하는 등 더 강한 관례가 있다. 여기서 bits는 payload 데이터다. 이 행동 규칙도 선언만으로 자동 assertion이 생성되는 것은 아니다. 타입의 이름을 보았다는 이유로 구현이 계약을 지켰다고 판단할 수 없다.

## 계약을 문장과 검사로 옮기기

프로젝트가 안정된 데이터 제시를 요구한다면 다음처럼 검사를 적을 수 있다.

```systemverilog
assert property (@(posedge clk) disable iff (!rst_n)
  valid && !ready |=> valid && $stable(data));
```

수락하지 못한 에지에 valid가 높았다면 다음 에지에도 valid와 데이터가 유지되어야 한다는 조건이다. 다음 에지에 ready가 높아 수락하더라도, 그때까지 같은 data를 제시해야 한다. reset이 계약을 끊는다고 가정했기 때문에 reset 동안은 검사하지 않는다. 취소 신호가 있는 인터페이스라면 그것이 이 의무를 언제 끝내는지도 별도로 정의해야 한다.

payload가 여러 필드로 늘어나면 data만 안정적이어서는 부족하다. 주소, 명령 종류, byte mask, 거래 식별자가 같은 거래에 속한다면 모두 함께 유지되어야 한다. 구조체를 쓰면 묶음 전체에 대한 안정성 검사를 표현하기 쉬워지지만, 한 필드를 빠뜨리지 않도록 설계 의도를 먼저 정해야 한다.

[lowRISC의 포트와 RTL 작성 지침](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md)은 폭과 명명, 모듈 경계를 일관되게 유지하는 데 도움이 된다. 다만 행동 규칙은 각 인터페이스의 요구를 바탕으로 추가해야 한다. 신호명이 모두 valid라고 해서 동일한 프로토콜을 뜻하는 것은 아니다.

## 추상화가 감추면 안 되는 시간 경계

같은 타입의 링크를 두 클록 도메인 사이에 곧바로 연결하는 것도 계약 문제다. 한 에지에서 두 신호를 함께 샘플링한다는 전제가 두 도메인에서는 성립하지 않는다. 폭과 방향이 맞더라도 전달과 수락을 각 도메인에서 어떻게 보장할지 필요하다. 인터페이스 추상화는 시간 경계를 지워 주지 않는다.

latency도 타입에서 빠지기 쉽다. “요청을 받으면 응답을 낸다”는 같은 함수형 설명이라도, 한 사이클 뒤 응답하는 블록과 임의의 시간 뒤 응답하는 블록은 다르게 연결해야 한다. 소비자가 응답을 특정 사이클에 기대한다면 버퍼나 완료 제어가 필요하다. 재사용하려는 블록의 포트가 같다는 이유로 제어 FSM까지 그대로 쓸 수 있다는 결론은 나오지 않는다.

공통화의 좋은 경계는 변하지 않는 계약을 함께 묶고, 달라질 수 있는 계약을 드러내는 곳이다. 예를 들어 payload 타입은 바꿀 수 있어도 수락 사건과 안정성 규칙은 고정할 수 있다. 반대로 서로 다른 안정성 규칙을 한 타입 이름에 숨기면 사용자가 내부 구현을 다시 읽어야 한다. 추상화의 이점이 사라진다.

처음의 연결 오류는 컴파일러가 놓친 한 문장이었다. “기다리는 동안 같은 데이터를 유지한다”는 소비자의 기대와 “수락할 때만 현재 값을 읽는다”는 생산자의 제공 범위가 달랐다. **하드웨어 인터페이스 계약을 완성하려면 폭과 방향 뒤에 수락 시점, 데이터 유지, reset과 취소의 경계를 함께 적어야 한다.** 그 규칙을 양쪽 모듈과 검사기가 공유할 때, 인터페이스 재사용은 배선의 재사용을 넘어 동작의 재사용이 된다.

