고수준 합성, C 코드가 RTL 회로가 되는 과정
과장이 “다음 주 임원 회의 자료를 준비해 두세요”라고 한 줄만 지시했다고 해 보겠습니다. 담당자는 어떤 자료를 누가 언제까지 모으고, 어떤 순서로 취합하고, 발표 직전 점검은 누가 맡을지를 적은 업무 절차서로 바꿔야 일이 굴러갑니다.
칩 설계에도 이런 번역 작업이 있습니다. 알고리즘을 C나 C++로 쓴 뒤 클록 사이클 단위로 움직이는 회로 설명으로 풀어 주는 고수준 합성(HLS, High-Level Synthesis)이 그것이고, 이 글에서는 도구가 무엇을 대신 정하는지와 그 한계를 차례로 짚어 보겠습니다.
사무실 비유와 다른 점도 있습니다. 사람 담당자는 상황을 보며 절차서를 융통성 있게 고치지만, 도구는 설계자가 준 클록 주기와 자원 제약 안에서 계산으로 절차서를 만들기 때문에 제약을 어떻게 주느냐가 결과를 크게 좌우합니다.
고수준 합성이란, 동작 기술에서 RTL을 만드는 일
HLS는 C, C++, SystemC 같은 언어로 쓴 동작 기술(Behavioral description)을 입력으로 받아 RTL을 출력하는 자동화 과정입니다. RTL(Register Transfer Level)은 어떤 값이 어느 클록 사이클에 어느 레지스터에서 어디로 옮겨 가는지를 적는 설계 수준입니다.
흐름으로 보면 HLS의 출력이 논리 합성의 입력이 됩니다. 논리 합성은 사이클 구조가 이미 정해진 RTL을 게이트로 바꾸는 일이고, HLS는 그 사이클 구조 자체를 만들어 내는 일이라서 한 단계 앞, 더 추상적인 자리에 놓입니다.
HLS가 놓이는 자리
- 동작 기술: 클록 개념 없이 C나 C++로 알고리즘을 씁니다.
- HLS: 연산을 사이클에 배치하고 필요한 하드웨어 자원을 정합니다.
- RTL: 상태 기계(FSM)와 데이터 경로가 만들어집니다.
- 논리 합성: RTL을 표준 셀로 이루어진 게이트 수준 넷리스트로 바꿉니다.
- 배치와 배선: 물리적인 위치와 연결이 정해집니다.
C 코드를 그대로 넣으면 알아서 최적의 병렬 회로가 나온다는 이해는 흔한 오해입니다. C 코드는 문장이 위에서 아래로 차례로 실행된다는 순차 모델이라서, 어디까지 동시에 처리할지는 설계자가 구조와 합성 지시문으로 알려 주어야 합니다.
RTL을 직접 쓰기 어려워지는 지점
RTL을 직접 쓰면 설계자가 상태 기계, 데이터 경로, 사이클별 타이밍을 모두 적어야 합니다. 영상 필터 하나만 해도 줄 버퍼, 계수 곱셈, 누적 덧셈이 사이클마다 맞물려야 해서 코드가 길어지고, 처리량을 두 배로 올리려면 구조를 새로 짜야 합니다.
두 번째는 검증 속도입니다. C나 C++ 모델은 클록 단위로 회로를 흉내 내는 시뮬레이션보다 보통 훨씬 빠르게 실행되므로, 알고리즘이 맞는지는 소프트웨어 수준에서 먼저 끝낼 수 있습니다. 같은 코드가 HLS의 입력이 되면 입력 모델과 생성된 RTL의 출력을 같은 테스트로 비교할 수도 있습니다.
세 번째는 구조 탐색입니다. 같은 알고리즘을 “느리지만 작게”와 “빠르지만 크게”로 여러 번 뽑아 비교하고 싶을 때, 코드를 새로 쓰는 대신 제약만 바꿔 다시 돌리면 됩니다. 인원 두 명으로 짠 절차서와 네 명으로 짠 절차서를 나란히 놓고 비교하는 것과 비슷하죠.
스케줄링, 할당, 바인딩이 하는 일
HLS가 대신 정하는 일은 크게 세 가지입니다. 스케줄링(Scheduling)은 각 연산을 몇 번째 클록 사이클에 실행할지, 할당(Allocation)은 곱셈기와 덧셈기를 몇 개 쓸지, 바인딩(Binding)은 각 연산을 어느 유닛에, 중간값을 어느 레지스터에 배정할지 정합니다.
사무실로 옮기면 일정표, 투입 인원 수, 일마다 담당자 지정에 해당합니다. 이 셋은 서로 영향을 주기 때문에 한 번씩 차례로 풀고 끝나지 않고 함께 조정됩니다.
간단한 예로 y = a×b + c×d + e×f + g×h를 계산한다고 해 보겠습니다. 아래 수치는 설명을 위한 가상 값이며, 레지스터의 지연과 셋업 시간은 계산을 단순하게 하려고 생략했습니다.
가상 계산 예시, 같은 식의 두 가지 구성클록 주기 5 ns, 곱셈 3.0 ns, 덧셈 1.5 ns로 가정합니다.
- 자원을 많이 쓰는 구성(곱셈기 4개, 덧셈기 3개): 1사이클에 곱셈 네 개를 동시에 하고 덧셈 한 단계까지 3.0 + 1.5 = 4.5 ns로 끝냅니다. 2사이클에 마지막 덧셈 1.5 ns를 하므로 총 2사이클, 10 ns입니다.
- 자원을 아끼는 구성(곱셈기 1개, 덧셈기 1개): 사이클마다 곱셈 한 개와 누적 덧셈 한 번, 3.0 + 1.5 = 4.5 ns를 수행합니다. 곱셈이 네 번이라 총 4사이클, 20 ns입니다.
- 한 사이클에 전부 담으려면: 3.0 + 1.5 + 1.5 = 6.0 ns가 필요하므로 클록 주기가 6 ns 이상이어야 합니다.
곱셈기 네 개와 한 개의 차이가 사이클 수 두 배의 차이로 나타납니다. 면적과 속도를 맞바꾸는 이 선택을 설계자가 클록 주기와 자원 개수 제약으로 알려 주면, 도구가 그에 맞는 스케줄을 찾습니다.
자원을 아끼는 구성에서는 곱셈기 하나가 네 연산을 돌려 쓰므로 입력을 고르는 멀티플렉서와 중간값을 담을 레지스터가 따라붙습니다. 자원 공유는 공짜가 아니라는 점도 바인딩이 함께 계산하는 비용입니다.
파이프라이닝으로 반복문을 매 클록 시작하기
반복문은 하드웨어로 만들 때 성능을 가장 크게 좌우하는 요소입니다. 반복 한 번이 6사이클 걸리고 반복이 100번이라면, 앞 반복이 끝나야 다음을 시작하는 방식은 600사이클이 필요합니다.
파이프라인으로 만들면 앞 반복이 끝나기 전에 다음 반복을 시작합니다. 새 반복을 시작하는 간격을 II(Initiation Interval)라 하고, 전체 사이클 수는 대략 (반복 수 − 1) × II + 반복 한 번의 지연으로 계산합니다.
가상 계산 예시, 반복 100번, 반복 1회 지연 6사이클II = 1이면 (100 − 1) × 1 + 6 = 105사이클입니다. 파이프라인이 없을 때의 600사이클과 비교하면 약 5.7배 빠릅니다.
II = 2로 밖에 못 맞추면 (100 − 1) × 2 + 6 = 204사이클로 늘어납니다.
II를 1로 맞추지 못하는 이유는 대체로 세 가지입니다. 이전 반복의 결과를 다음 반복이 써야 하는 누적합 같은 의존이 있으면 그 연산 지연이 II의 하한이 되고, 한 사이클에 읽을 수 있는 메모리 포트 수가 모자라면 반복마다 필요한 읽기를 나누어 해야 하며, 곱셈기 같은 자원이 모자라도 마찬가지입니다.
대책은 배열을 여러 메모리로 나누어 한꺼번에 읽게 하거나, 반복문을 펼쳐 한 번에 여러 데이터를 처리하는 것입니다. 이런 선택은 모두 면적을 늘리는 대가로 처리량을 얻는 방향이어서, 클록 주기와 자원 제약을 함께 조절하면서 균형을 찾습니다.
고수준 합성이 쓰이는 곳과 쓰이지 않는 곳
HLS가 잘 맞는 곳은 연산 구조가 규칙적이고 데이터가 흐르듯 처리되는 블록입니다. 영상과 이미지 필터, 압축 처리의 일부 연산, 통신과 신호처리의 필터나 푸리에 변환, 행렬 곱처럼 같은 연산을 많은 데이터에 반복하는 가속기 커널이 대표적입니다.
FPGA에서 알고리즘을 가속할 때 널리 쓰이고, 기계학습 추론 가속기의 구조를 여러 가지로 바꿔 보는 프로토타이핑에도 쓰입니다. ASIC에서도 영상과 신호처리 블록에 쓰이는 사례가 있지만, 도입 정도는 회사와 블록에 따라 차이가 큽니다.
반대로 복잡한 제어 로직, 프로토콜 인터페이스, 클록과 리셋과 전력 도메인의 구조는 RTL로 직접 쓰는 경우가 많습니다. 사이클 단위의 동작을 정확히 통제해야 하는 영역이고, 클록 도메인 사이를 건너는 방식이나 리셋을 푸는 순서 같은 시스템 수준의 약속은 도구가 대신 정해 주지 않기 때문입니다.
QoR 차이와 코딩 스타일 의존이라는 한계
가장 자주 거론되는 한계는 QoR(Quality of Results, 결과 품질), 즉 면적, 속도, 전력의 차이입니다. 숙련자가 직접 쓴 RTL과 비슷한 결과가 나오는 블록도 있고 크게 벌어지는 블록도 있어서, 어느 쪽이 될지는 블록의 성격과 입력 코드를 쓰는 방식에 달려 있습니다.
코드 작성 방식에 대한 의존도 큽니다. 재귀, 실행 중 메모리 할당, 임의의 포인터 연산처럼 하드웨어로 옮기기 어려운 문법은 제한되거나 지원되지 않는 경우가 많습니다. 같은 알고리즘이라도 배열을 읽는 순서와 반복 횟수를 상수로 두었는지에 따라 만들어지는 회로가 크게 달라집니다.
타이밍 예측도 한계가 있습니다. 스케줄링은 연산별 지연 추정치를 바탕으로 하는데, 실제 배선 지연은 배치와 배선을 마친 뒤에야 알 수 있어서 추정과 최종 결과가 어긋날 수 있습니다. 생성된 RTL은 사람이 읽기 어려운 경우가 많아, 문제가 생기면 원래 코드와 어떻게 대응되는지 따라가며 디버깅해야 합니다.
RTL 직접 설계와 HLS 비교 표
| 구분 | RTL 직접 설계 | HLS |
|---|---|---|
| 입력 | 사이클 단위로 적은 RTL | C, C++ 같은 동작 기술 |
| 사이클 구조 | 설계자가 직접 정합니다. | 도구가 제약 안에서 정하고 설계자는 클록 주기, 자원, 지시문으로 유도합니다. |
| 알고리즘 검증 | RTL 시뮬레이션 중심 | 소프트웨어 수준에서 빠르게 검증한 뒤 RTL 결과와 비교 |
| 구조 변경 | 코드 수정 범위가 큽니다. | 제약과 지시문을 바꿔 여러 구조를 생성 |
| 결과 제어 | 세밀하게 직접 통제 | 간접적이어서 품질 편차가 생길 수 있음 |
| 잘 맞는 블록 | 제어 로직, 인터페이스, 타이밍이 빠듯한 블록 | 규칙적인 데이터 흐름의 연산 커널 |
핵심 정리
고수준 합성은 클록이 없는 동작 기술에서 사이클별 동작을 가진 RTL을 만들어 주는 도구입니다. 스케줄링, 할당, 바인딩으로 연산의 시간과 자원을 정하고, 파이프라인으로 처리량을 끌어올리며, 그 결과는 면적과 속도의 균형으로 나타납니다.
업무 절차서 비유로 돌아가면, 과장의 한 줄 지시만으로는 일이 돌아가지 않듯 C 코드만으로도 좋은 회로가 나오지 않습니다. 설계자가 클록 주기와 자원, 반복 구조를 알려 주고 결과 RTL을 점검해야 하고, 규칙적인 연산 블록에는 HLS를, 사이클을 엄격히 통제해야 하는 부분에는 RTL을 쓰는 식으로 섞어 쓰는 것이 현실적인 접근입니다.
면접 대비 질문
면접 대비 질문HLS에서 스케줄링과 바인딩이 각각 무엇을 정하는지 설명하고, 직접 RTL을 쓰는 방식과 비교해 장점과 한계를 말해 보세요.
[답변 포인트] 스케줄링은 연산을 몇 번째 클록 사이클에 실행할지, 바인딩은 연산과 중간값을 어떤 연산 유닛과 레지스터에 배정할지 정하며 할당과 함께 면적과 지연의 균형을 만든다고 설명합니다. 곱셈기 개수와 사이클 수를 맞바꾸는 간단한 예로 자원 공유의 비용(멀티플렉서와 레지스터 증가)까지 짚으면 좋습니다. 장점은 알고리즘 검증이 빠르고 구조 탐색이 쉽다는 점, 한계는 QoR 편차, 코딩 스타일 의존, 배선 지연 추정의 부정확성, 제어 로직과 인터페이스는 여전히 RTL이 유리하다는 점으로 정리합니다.









