목차
- 오픈소스 HLS(High-Level Synthesis)란?
- 오픈소스 HLS 프로젝트 – Google XLS 알아보기
- 오픈소스 HLS 프로젝트 – LLVM CIRCT 알아보기
- 오픈소스 HLS: XLS와 CIRCT 비교
- 정리
Intro
예전에 Catapult HLS를 사용하면서 C++ 코드를 RTL로 합성해본 적이 있다. 그래서 오픈소스 HLS를 찾아볼 때도 새로운 언어보다는 기존 C++ 코드를 받아주는 frontend가 먼저 눈에 들어왔다.
찾아보니 Google에서 개발하는 XLS라는 프로젝트가 있고, LLVM 쪽에는 CIRCT라는 프로젝트가 있다. CIRCT는 이름 그대로 circuit을 떠올리면 된다. 둘 다 프로그램을 하드웨어로 바꾸는 일을 다루지만, 막상 살펴보면 접근 방법은 꽤 다르다.
이번 포스팅에서는 두 프로젝트가 무엇인지 큰 그림을 먼저 보고, XLS는 C++ frontend인 XLS[cc]를 중심으로 알아보려고 한다.

오픈소스 HLS(High-Level Synthesis)란?
HLS는 High-Level Synthesis의 약자다. Verilog나 VHDL로 RTL을 직접 작성하는 대신, C/C++ 같은 높은 수준의 언어로 알고리즘을 작성하고 컴파일러가 회로를 만들어주는 방식이다.
그럼 C 코드를 Verilog 문법으로 대충 번역해주는 건가? 싶을 수 있는데, 그렇게 간단하지는 않다. 소프트웨어는 이미 만들어진 CPU 위에서 실행되지만 HLS는 프로그램을 실행할 하드웨어 자체를 만들어야 한다.
- Scheduling: 각 연산을 몇 번째 clock에 실행할지 정한다.
- Resource allocation: adder, multiplier, memory 같은 자원을 몇 개 사용할지 정한다.
- Pipelining: 연산 사이에 register를 넣어 처리량을 높인다.
- RTL generation: 결정된 구조를 Verilog나 SystemVerilog로 출력한다.
같은 덧셈과 곱셈 코드라도 목표 clock이 얼마인지, 연산기를 공유할 것인지, pipeline을 몇 단계로 만들 것인지에 따라 결과 회로는 달라진다. 그러니까 HLS에서 중요한 것은 언어 번역만이 아니라 시간과 자원을 하드웨어에 배치하는 과정이라고 볼 수 있다.
오픈소스 HLS는 뭐가 좋을까?
Vitis HLS 같은 상용 도구는 FPGA 프로젝트에 바로 연결할 수 있고 사용하기도 편하다. 하지만 내부에서 코드를 어떤 형태로 바꾸고, 어떤 최적화를 거쳐 RTL을 만드는지는 자세히 보기 어렵다.
반면 오픈소스 프로젝트는 frontend, IR(Intermediate Representation), optimization pass, scheduler, RTL code generator를 직접 들여다볼 수 있다. HLS 컴파일러가 회로를 만드는 과정을 공부하거나 새로운 최적화를 실험하기에는 이쪽이 훨씬 재미있어 보인다.
오픈소스 HLS 프로젝트 – Google XLS 알아보기
XLS(Accelerated HW Synthesis)는 Google에서 공개한 Apache 2 라이선스의 HLS toolchain이다. 공식 문서에서는 높은 수준으로 작성한 기능을 합성 가능한 Verilog 또는 SystemVerilog로 만들어주는 도구라고 설명한다.
처음에는 XLS니까 Excel과 관련 있나 싶지만 전혀 아니다. 여기서 XLS는 Accelerated HW Synthesis라는 이름을 사용한다.
XLS[cc]와 XLS IR
XLS에는 Rust와 비슷한 문법의 DSLX라는 전용 언어가 있다. 하지만 Catapult HLS처럼 C++로 HLS를 사용했던 입장에서는 XLS[cc] 쪽이 좀 더 익숙하다. 이번 포스팅에서도 DSLX보다는 XLS[cc]를 기준으로 살펴보려고 한다.
XLS[cc]는 C++ 코드의 일부를 XLS IR로 변환하는 XLS의 C++ HLS frontend다. libclang을 기반으로 해서 C++17 문법 대부분을 해석할 수 있지만, 모든 C++ 코드를 합성할 수 있다는 뜻은 아니다.
- pointer
- function pointer
- virtual method
위 기능들은 공식 README에서 지원하지 않는다고 명시되어 있다. Catapult HLS에서도 합성을 생각하면서 C++ 코드를 작성해야 했듯이, XLS[cc] 역시 일반적인 software용 C++ 코드를 아무 수정 없이 RTL로 바꾸는 도구는 아니다.
C++에서 Verilog까지
XLS[cc]는 하나의 .cc translation unit을 입력으로 받는다. 합성할 top function에는 #pragma hls_top을 붙이고, xlscc로 XLS IR을 만든다.
C++ / #pragma hls_top
↓
XLS[cc]
↓
XLS IR
↓
opt_main
↓
codegen_main
↓
Verilog / SystemVerilog
opt_main은 XLS IR을 최적화하고, codegen_main은 최적화된 IR에서 combinational 또는 pipelined RTL을 생성한다. C++ frontend와 optimization, scheduling, code generation이 XLS IR을 중심으로 분리되어 있는 구조다.
Catapult HLS와 비교하면 C++에서 시작한다는 점은 익숙하지만 내부 흐름은 꽤 다르다. 특히 XLS IR이 중간에 드러나 있고 각 tool을 따로 실행할 수 있어서, C++ 코드가 어떤 IR로 바뀌고 최종 RTL까지 내려가는지 직접 따라가기 좋아 보인다.
XLS[cc]의 위치
다만 XLS[cc]는 XLS의 기본 frontend라기보다는 contrib 영역에 있는 별도 frontend다. 공식 README에도 원 기여자가 주로 관리하고 있으며, XLS core 개발팀은 best-effort 수준으로만 유지한다고 적혀 있다. 따라서 XLS 전체와 XLS[cc]의 완성도를 같은 것으로 보면 안 될 것 같다.
그래도 C++ HLS를 사용해본 입장에서는 XLS를 들여다보기 좋은 입구다. 기존 C++ 설계를 어디까지 받아주는지, pragma와 type을 어떻게 해석하는지, 필요한 기능을 추가하려면 어디를 바꿔야 하는지는 별도로 정리해보려고 한다.
오픈소스 HLS 프로젝트 – LLVM CIRCT 알아보기
CIRCT는 Circuit IR Compilers and Tools의 약자다. LLVM 프로젝트 생태계에서 MLIR과 LLVM의 개발 방법을 하드웨어 설계 도구에 적용하고 있다.
그럼 CIRCT도 XLS처럼 코드를 넣으면 RTL이 나오는 HLS 프로그램인가? 반은 맞고 반은 다르다. CIRCT에는 HLS flow가 있지만, 프로젝트 전체로 보면 하나의 완성된 HLS 제품보다는 하드웨어 컴파일러를 만들기 위한 공통 기반에 가깝다.
MLIR dialect가 뭔가?
일반적인 컴파일러도 source code를 바로 machine code로 바꾸지 않고 여러 단계의 IR을 거친다. 하드웨어도 마찬가지인데, high-level algorithm과 RTL 구조는 표현해야 하는 정보가 너무 다르다.
CIRCT는 이걸 하나의 만능 IR에 모두 넣기보다 목적별 dialect로 나눈다. 각 dialect는 특정 단계의 회로나 동작을 표현하고, pass를 이용해 점점 낮은 단계로 lowering한다.
- Handshake: 연산 사이의 data flow와 handshake를 표현한다.
- HW: module, port, instance 같은 공통 hardware 구조를 표현한다.
- Comb / Seq: combinational logic과 sequential logic을 표현한다.
- SV: SystemVerilog에 가까운 구조와 code generation을 담당한다.
- FIRRTL: Chisel 생태계의 FIRRTL 표현을 처리한다.
- Calyx: control과 datapath를 명시적으로 구성하는 IR을 지원한다.
dialect가 이것보다 훨씬 많아서 처음 보면 좀 막막하다. 그래도 큰 흐름은 high-level IR을 여러 pass로 변환해서 HW, Comb, Seq, SV처럼 RTL에 가까운 표현으로 내려간다고 이해하면 될 것 같다.
CIRCT의 HLS flow
CIRCT 안에는 한 가지가 아니라 여러 HLS flow가 함께 개발되고 있다. 공식 HLS 문서를 보면 hlstool이라는 driver가 MLIR과 CIRCT의 pass를 pipeline으로 연결해서 HLS flow를 실행한다.
예를 들어 dynamically scheduled HLS(DHLS)는 control flow 수준의 코드를 Handshake IR로 바꾸고, 최종적으로 Verilog까지 lowering한다. 전체 pipeline을 한 번에 실행할 수도 있고, circt-opt로 특정 IR과 pass만 따로 돌려볼 수도 있다.
사용자 입장에서는 복잡해 보이지만 compiler를 연구하거나 직접 만드는 입장에서는 꽤 유연한 구조다. frontend나 scheduling 방법을 새로 만들더라도 모든 backend를 처음부터 다시 만들 필요가 없기 때문이다.
오픈소스 HLS: XLS와 CIRCT 비교
여기까지 보면 둘 다 IR을 사용하고 Verilog를 생성하니까 비슷해 보인다. 하지만 프로젝트를 사용하는 관점에서는 아래와 같은 차이가 있다.
| 구분 | Google XLS | LLVM CIRCT |
|---|---|---|
| 성격 | 고수준 설계부터 RTL까지 연결된 HLS toolchain | 하드웨어 compiler를 만들기 위한 MLIR 기반 infrastructure |
| 주요 입력 | C++ subset(XLS[cc]), DSLX, XLS IR | 여러 frontend와 MLIR dialect |
| 중간 표현 | XLS 전용 IR | 목적에 따라 나뉜 여러 CIRCT/MLIR dialect |
| 동시성 표현 | channel과 state를 가진 proc | Handshake, Calyx 등 여러 접근 방법 |
| 먼저 해볼 것 | C++ 예제를 XLS IR과 SystemVerilog로 변환 | dialect 예제와 lowering pass 확인 |
| 적합한 용도 | XLS의 일관된 합성 flow로 hardware IP 개발 및 실험 | 새로운 hardware language, IR, optimization, HLS flow 개발 |
대충 정리하면 XLS는 직접 설계를 작성하고 RTL까지 만들어보는 하나의 toolchain에 가깝다. CIRCT는 여러 종류의 하드웨어 compiler를 만들 수 있도록 IR과 변환 도구를 모아놓은 framework에 가깝다.
Catapult HLS 경험을 이어서 본다면 XLS[cc]로 간단한 C++ 코드를 XLS IR과 SystemVerilog로 변환해보는 흐름이 먼저 눈에 들어온다. CIRCT는 그 다음에 dialect가 어떻게 연결되고 lowering되는지 하나씩 보는 편이 좋을 것 같다.
아직은 실험적인 프로젝트
그럼 지금 바로 Vitis HLS를 지우고 XLS나 CIRCT를 실무에 사용하면 될까? 그건 아닌 것 같다.
XLS 공식 문서에서는 프로젝트가 experimental 상태이고 빠르게 개발되고 있다고 밝히고 있다. XLS[cc]도 contrib 영역에서 best-effort로 유지되는 frontend다. CIRCT의 HLS 문서 역시 여러 flow가 활발하게 개발 중이지만 production-ready로 보면 안 된다고 분명하게 적어두었다.
당장 상용 도구를 대체하기보다는 HLS compiler의 내부를 공부하고 새로운 방법을 실험하는 프로젝트로 보는 것이 맞다. 뭐.. 오히려 공부하는 입장에서는 완성된 GUI 뒤에 가려진 IR과 pass를 직접 볼 수 있으니 더 재미있을 수도 있다.
정리
이번에는 오픈소스 HLS 프로젝트인 XLS와 CIRCT가 어떤 프로젝트인지 대략 알아보았다.
- XLS[cc]는 C++ 코드의 일부를 XLS IR로 변환하는 HLS frontend다.
- CIRCT는 MLIR dialect와 pass를 이용해서 hardware compiler를 만들기 위한 infrastructure다.
- 둘 다 아직 실험적인 부분이 많지만 HLS 내부 구조를 공부하기에는 재미있어 보인다.
나는 DSLX보다는 XLS[cc]를 먼저 살펴보려고 한다. Catapult HLS에서 사용하던 C++ 코드와 비교해보고, 필요한 기능을 추가하기 위해 XLS[cc]를 개조한 과정은 별도로 정리할 생각이다.