---
title: "모듈 경계를 없애야만 상수 전파가 되는 것은 아니다"
author: "브레이브브라우저쓰자"
published_at: "2026-10-22T00:50:01.938Z"
updated_at: "2026-10-06T01:33:53.278Z"
tags: ["hierarchy flattening"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%EB%AA%A8%EB%93%88-%EA%B2%BD%EA%B3%84%EB%A5%BC-%EC%97%86%EC%95%A0%EC%95%BC%EB%A7%8C-%EC%83%81%EC%88%98-%EC%A0%84%ED%8C%8C%EA%B0%80-%EB%90%98%EB%8A%94-%EA%B2%83%EC%9D%80-%EC%95%84%EB%8B%88%EB%8B%A4"
ai_assisted: true
---

# 모듈 경계를 없애야만 상수 전파가 되는 것은 아니다

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

[원문](https://ariser.io/@writer_a9817d5a8de0/%EB%AA%A8%EB%93%88-%EA%B2%BD%EA%B3%84%EB%A5%BC-%EC%97%86%EC%95%A0%EC%95%BC%EB%A7%8C-%EC%83%81%EC%88%98-%EC%A0%84%ED%8C%8C%EA%B0%80-%EB%90%98%EB%8A%94-%EA%B2%83%EC%9D%80-%EC%95%84%EB%8B%88%EB%8B%A4)

작은 모듈을 여러 개 연결한 RTL을 합성했더니, 모듈 경계를 없앤 결과에서만 덧셈기가 사라졌다. 모듈을 나눈 설계는 최적화를 방해하는 걸까? hierarchy flattening은 하위 모듈의 내부 회로를 상위 연결 안으로 펼치는 변환이다. **경계를 없애면 연결 조건이 직접 보이지만, 모든 경계가 반드시 최적화 장벽인 것은 아니다.**

## 재사용 모듈의 입력과 인스턴스의 입력은 다르다

부호 없는 8비트 a·b와 mode를 받아 `y = mode ? (a+b) : a`를 계산하는 하위 모듈을 생각하자. 합은 8비트로 잘라 내보낸다고 정한다. 이 모듈을 임의의 입력을 받는 독립 부품으로 합성한다면 mode=1인 경우도 필요하므로 덧셈 동작을 없앨 수 없다.

상위에서는 mode를 0에 묶어 이 모듈을 한 번만 사용한다고 하자. 실제로 사용하는 인스턴스의 결과는 모든 a·b에서 y=a다. b와 덧셈의 결과는 상위 출력에 영향을 주지 않는다. 모듈의 일반 기능과 상위에서 요구하는 특수한 기능은 다르다.

상위와 하위를 각각 따로 최적화하는 흐름에서는 이 사실을 한쪽만 보고 판단하기 어렵다. 상위는 mode가 0이라는 연결을 알지만 하위 연산은 경계 안에 있다. 하위는 덧셈을 알지만 mode를 임의 입력으로 본다. flatten으로 두 표현을 펼친 뒤 상수 선택을 정리하면 y=a라는 관계가 같은 회로 안에 드러난다.

Yosys의 flatten 설명은 셀을 현재 설계 안의 구현으로 대체한다고 정의한다. 내부 회로를 가져와 연결하는 변환이지, 그 자체가 모든 상수와 미사용 연산을 최적화해 없애는 명령은 아니다. 펼친 뒤 어떤 최적화 패스를 적용했는지까지 함께 보아야 한다. [Yosys flatten의 공식 설명](https://yosyshq.readthedocs.io/projects/yosys/en/stable/cmd/index_passes_hierarchy.html#flatten-flatten-design)

## 경계를 남겨도 전달할 수 있는 정보

위 사례에서 mode=0을 하위에 전달할 수 있는 최적화가 있다면, 경계를 반드시 없앨 필요는 없다. Yosys의 opt_hier는 상수, 미사용 신호와 함께 묶인 신호의 정보를 모듈 경계 너머로 전달한다. opt의 hier 옵션을 쓰는 흐름도 공식 명령 설명에 나와 있다. 따라서 “flatten하지 않으면 상수 전파는 불가능하다”는 결론은 지나치다. [Yosys의 경계 간 최적화](https://yosyshq.readthedocs.io/projects/yosys/en/stable/cmd/index_passes_opt.html#opt-hier-perform-cross-boundary-optimization)

다만 재사용 조건을 살펴야 한다. 같은 하위 모듈을 mode=0인 인스턴스와 mode=1인 인스턴스에서 함께 쓴다면, 하위 모듈 전체의 mode를 0으로 고정할 수는 없다. 첫 인스턴스는 a만 출력하지만 둘째는 a+b를 출력해야 한다.

예를 들어 a=250, b=10이면 첫 결과는 250, 둘째는 8비트 합인 4다. 하위 모듈 정의 하나를 첫 인스턴스만 보고 고치면 둘째 결과가 틀린다. 인스턴스별로 펼치거나, 별도 특수화한 구현을 만드는 흐름이 이 차이를 다룰 수 있다. 어느 방법을 사용했는지와 모든 사용처의 조건을 확인해야 한다.

실제 opt_hier 구현도 하위 모듈의 사용 정보와 연결을 모아 판단한다. 공식 설명은 최상위가 아닌 모듈의 포트 의미가 바뀔 수 있다고 주의를 준다. 상위 연결 조건을 반영해 최적화한 하위 모듈을, 원래처럼 모든 입력을 받는 독립 부품으로 다시 사용할 수 있다고 간주해서는 안 된다. [모듈 사용 정보를 모으는 opt_hier 구현](https://github.com/YosysHQ/yosys/blob/main/passes/opt/opt_hier.cc)

## 없앤 경계가 검증과 디버깅에 남기는 일

모듈 경계는 사람이 설계를 이해하고 재사용하는 단위이기도 하다. 경계를 펼치면 여러 인스턴스의 내부 연결을 한꺼번에 최적화할 기회가 생기지만, 원래의 계층 이름과 중간 신호를 그대로 따라가기는 어려워질 수 있다. 논리 복제나 공유까지 진행되면 이전에 한 모듈 안에 있던 기능이 여러 위치로 나뉠 수도 있다.

그렇다고 flatten이 모든 출처 정보를 지운다는 뜻은 아니다. Yosys는 기본적으로 scopeinfo 셀 등으로 제거된 모듈과 셀의 속성을 남기는 옵션 체계를 제공한다. 필요한 경우 enclosing scope 이름을 남기는 설정도 있다. 원래 구조를 완전히 유지하는 것과, 펼친 회로에서 원래 RTL의 위치를 찾을 정보를 보존하는 것은 다른 선택이다.

반대로 keep_hierarchy를 지정한 셀이나 모듈은 flatten이 펼치지 않도록 할 수 있다. 그 속성은 해당 변환의 경계를 제어하는 것이지, 내부 회로의 모든 최적화를 정지시키거나 모든 최적화 도구에 같은 뜻을 전달하는 보편적인 약속은 아니다. 명령과 속성이 실제로 보존하는 범위를 읽는다.

기능 검증에서는 최상위의 입출력 계약을 기준으로 전후 결과를 확인해야 한다. 위 예라면 mode=0 인스턴스가 모든 입력에서 a를 그대로 내보내고, mode=1 인스턴스가 모듈로 256 합을 내보내는지 검사한다. 경계가 사라졌다는 이유로 비교 대상이던 출력이나 초기·리셋 조건을 생략할 수는 없다.

모듈을 잘 나눈 RTL을 유지하면서도 필요한 최적화를 할 수 있다. 먼저 인스턴스의 상수 연결과 재사용 조건을 확인하고, 경계 너머로 전달하는 정보가 충분한지 본다. 부족한 부분에 flatten이나 특수화를 적용한 뒤 디버그 출처와 기능 관계를 검증하면 된다. 중요한 선택은 모듈 수를 줄이는 것이 아니라, **최적화에 필요한 문맥을 어떤 방식으로 보이게 만들 것인가**다.

