CIRCT의 목표와 하드웨어 설계를 위한 방언 및 도구 흐름을 설명합니다.
최근 컴퓨터 아키텍처의 추세는 두 가지 핵심 문제를 낳았습니다. 첫째, 범용 구성 요소와 특수 구성 요소를 혼합한 복잡하고 이기종인 시스템 온 칩을 어떻게 설계할 것인가? 둘째, 이를 어떻게 프로그래밍할 것인가? 우리는 매우 다양한 추상화를 표현하고 조작하는 설계 도구가 이러한 문제를 해결하는 데 핵심이라고 믿습니다. 이 프로젝트는 LLVM/MLIR을 사용하여 이러한 추상화를 표현하고, 이를 기반으로 향후 10년의 설계 문제를 해결할 수 있는 사용 가능한 오픈 소스 흐름을 구축하는 데 중점을 둡니다.
반도체 공정 기술의 스케일링 속도가 둔화되면서, 더욱 특수화된 아키텍처를 개발하는 방향으로 광범위한 전환이 이루어졌습니다. 이러한 특수화된 아키텍처를 구현하는 회로는 일반적으로 _가속기_라고 부르지만, 성능, 전력 사용량, 설계 비용 및 제조 비용 간의 절충을 바탕으로 개발됩니다. 가속기는 일반적으로 특정 워크로드에 최적화된 연산 구조, 인터커넥트 및 메모리 계층 구조를 구현합니다. 전체 설계 목표를 위해서는 연산 구조를 최적화하는 것보다 메모리와 통신을 최적화하는 일이 더 중요한 경우가 많습니다. 가속기는 유사한 워크로드 집합에서 동작할 수 있도록 어느 정도 프로그래밍 가능하게 설계되는 것이 일반적이지만, 보통 범용 아키텍처보다 프로그래밍 가능성이 훨씬 낮습니다.
가속기는 특수화 덕분에 매우 가치가 있는 경우가 많으므로, 지속적으로 사용되지 않더라도 장치에 포함될 수 있습니다.
특수화의 필요성으로 인해 기계 학습과 같은 일부 분야에서는 매우 다양한 가속기가 등장했습니다. 광범위한 설계 언어로 다양한 가속기를 프로그래밍하는 일은 중대한 과제입니다. MLIR은 _방언_이라고 하는 여러 추상화를 사용하여 이를 해결합니다. 개별 설계 언어는 일반적으로 각자의 방언과 연관되며, 이 방언들은 대체로 공통 방언으로 낮아지고 최적화됩니다. 공통 방언에서 최적화를 구현하면 최적화 개발 및 유지 관리 노력을 설계 언어와 대상 아키텍처 사이에서 공유할 수 있습니다. 궁극적으로 방언은 저수준 방언으로, 그리고 최종적으로 대상별 방언으로 낮아질 수 있으며(필요에 따라 대상별 방식으로), 이를 통해 특정 가속기 대상을 위한 코드 생성을 용이하게 합니다.
동시에 가속기 자체를 개발하는 일도 더욱 어려워지고 있습니다. 가속기는 흔히 복잡하며 충족하기 어려운 여러 설계 제약 조건을 가질 수 있습니다. 하나의 가속기만 구축해도 상당한 비용이 들 수 있습니다. 한편 가속기의 수는 많으므로 각 가속기에 투입할 수 있는 설계 노력은 줄어들지만, 가속기 구축 비용은 계속해서 반복적으로 발생합니다. 우리는 하드웨어 설계 복잡성을 해결할 수 있는 MLIR의 근본 개념이 가속기를 프로그래밍하는 과정만큼이나 가속기를 구축하는 과정에도 적용된다고 주장합니다. 나아가 가속기 설계와 가속기 프로그래밍을 위한 추상화를 통합하는 것은 임의의 가속기를 위한 프로그래밍을 가능하게 하는 핵심 단계입니다.
기존 시스템에서는 일반적으로 사용되는 매우 다양한 추상화를 볼 수 있습니다. 이러한 추상화는 설계 프로세스의 서로 다른 부분에서 사용되는 추상화 계층 구조로 존재하는 경향이 있으며, 상위 수준의 추상화는 하위 수준의 추상화를 생성하는 데 사용됩니다. 이 계층 구조는 도구에서 명시적으로 드러나지 않는 경우가 많고, 인간 엔지니어가 적용하는 비공식적 프로세스에서만 분명해집니다. 예를 들어 엔지니어는 화이트보드의 블록 다이어그램을 사용하여 일련의 가속기를 통과하는 데이터 흐름을 논의하고, 그 아키텍처를 비공식 문서에 기록한 뒤 하드웨어 설계 언어로 이러한 구성 요소를 구현하고 통합할 수 있습니다. 우리는 방언을 사용하여 하드웨어 설계 추상화를 정의하는 데 MLIR을 활용하면 이러한 명세가 더 정확해지고, 이러한 추상화 간 변환을 더 많이 자동화할 수 있다고 믿습니다.
이러한 추상화는 설계 프로세스의 서로 다른 시점에서 서로 다른 방식으로 사용될 수 있다는 점에 유의해야 합니다. 이러한 사용 방식의 차이는 흔히 구성 요소 세분성의 차이로 귀결됩니다. 예를 들어 설계 프로세스 초기에 하나의 추상화는 크고 거친 세분성의 구성 요소와 함께 사용될 수 있습니다. 다른 추상화를 거쳐 낮아진 뒤에는 동일한 추상화가 더 작고 세밀한 세분성의 구성 요소와 함께 다시 나타날 수 있습니다.
아래에서는 기존 프로세스에서 사용되는 가장 중요한 추상화를 요약합니다. 이 목록은 포괄적이지 않을 가능성이 높으며, 이러한 추상화를 MLIR 방언으로 포착하는 과정에서 방언 간 낮춤을 용이하게 하는 추가 추상화나 제약 조건이 발견될 가능성이 높습니다. 많은 기존 시스템이 여러 추상화에 걸쳐 있지만, 여기서는 각 도구에서 사용되는 가장 중요한 내부 표현에 초점을 맞추고자 했습니다.
넷리스트는 회로 설계에 오랫동안 사용된 추상화로, 기본 구성 요소의 인스턴스화와 상호 연결을 표현합니다. 구성 요소는 일반적으로 세밀한 단위이며, 표준 셀 집적 회로의 논리 게이트나 프로그래밍 가능 논리의 아키텍처 기본 요소를 나타냅니다. EDIF 형식은 전자 설계 자동화 초기부터 거의 어디에나 사용되어 왔으며, 현대 도구에서는 넷리스트를 저장하는 데 Verilog 언어도 흔히 사용됩니다.
넷리스트 기술은 구성 요소의 입력과 출력 간 구분을 흐리는 경우가 많습니다. 구성 요소 간 연결은 여러 구성 요소가 동일한 와이어에 신호를 구동할 수 있는 물리적 연결을 나타낼 수 있습니다(바라건대 서로 다른 시점에!). 또는 하나의 구성 요소가 때로는 와이어를 입력으로 사용하고, 다른 때에는 동일한 와이어를 출력으로 사용할 수 있습니다.
레지스터 전송 수준은 논리 회로를 기술하는 데 일반적으로 사용됩니다. 논리 합성 프로세스는 보통 RTL 설계를 구조적 넷리스트로 낮추는 데 사용됩니다. 업계에서는 Verilog, SystemVerilog 및 VHDL이 널리 사용됩니다. LLHD 프레임워크는 RTL 설계를 포착하고 이를 효율적으로 시뮬레이션하거나 합성할 수 있는 보다 근본적인 구성체로 낮추기 위해 명시적으로 다단계 접근 방식을 취합니다. 이 시스템의 핵심 측면은 유용한 입력 언어(SystemVerilog 등)가 쉽게 합성할 수 있는 범위보다 훨씬 넓은 구성체 집합을 제공하며, 이러한 복잡성을 구조적인 방식으로 관리하는 것이 실제 시스템 구축의 핵심이라는 관찰입니다. RTL 수준 설계를 개선하는 데 중점을 둔 또 다른 최근 시스템은 관련 FIRRTL 중간 표현과 함께 사용되는 Chisel입니다.
레지스터 전송 수준에서는 클록이 있는 구성 요소(즉, 레지스터)와 클록이 없는 구성 요소(즉, 조합 논리)의 구분이 명시적입니다. 많은 회로 합성 기법은 역사적으로 이 구분을 활용하여 조합적 측면에만 집중했습니다. 현대 회로 분석 기법은 흔히 동기 경계를 가로지르는 동작을 고려합니다. 리타이밍은 RTL 기술에 수행되는 일반적인 변환입니다.
C 코드로부터 RTL 설계를 수행하는 고수준 합성(HLS)은 가속기 설계를 위한 일반적인 패러다임이 되었습니다. 일반적으로 고성능 구현을 생성하려면 특정 선택이 성능에 미치는 영향을 이해하기 위해 FSMD 모델의 분석과 스케줄링이 필요합니다.
HLS에서 흔한 중간 추상화는 유한 상태 기계와 데이터패스 추상화의 결합입니다. 개념적으로 상태 기계의 각 상태에서는 데이터패스의 특정 연산이 활성화됩니다. 이 추상화는 자원 공유와 파이프라인 동작에 대한 추론을 용이하게 하는 한편, 그러한 메커니즘이 구현되는 구체적인 방식은 암묵적으로 남겨 둡니다. RTL 수준 기술로 낮추려면 일반적으로 이러한 메커니즘을 명시적으로 표현하여 제어 유한 상태 기계와 데이터패스를 모두 레지스터 및 조합 논리로 변환해야 합니다. 이 낮춤에서는 회로의 클록과 클록 인에이블도 효율적인 방식으로 명시적으로 표현해야 합니다. 유한 상태 기계는 고정 논리로 논리 합성하여 낮추는 대신 다른 방식(예를 들어 마이크로코드 사용)으로 표현될 수도 있다는 점에 유의해야 합니다.
여러 프로젝트가 HLS 컴파일러의 내부 표현을 기술했습니다. LegUp은 설계 표현을 위해 Clang/LLVM 프레임워크를 활용하는 반면, Bambu는 독립적인 내부 표현을 구축합니다.
Calyx는 소프트웨어와 유사한 제어 언어와 하드웨어와 유사한 구조 언어를 결합한 새로운 중간 언어입니다. 이 중간 수준 표현을 통해 Calyx는 제어 흐름 정보를 사용하여 구조적 프로그램을 최적화할 수 있습니다. Calyx는 현재 Dahlia와 여러 다른 도메인 특화 아키텍처(시스토릭 배열, 수론 변환 장치)의 컴파일 대상 역할을 합니다.
데이터플로우 모델은 병렬 연산을 표현하기 위해 오랫동안 사용되어 왔습니다. 일반적으로 이러한 모델에는 데이터 스트림을 통해 통신하는 독립 프로세스를 나타내는 구성 요소가 포함됩니다. 이러한 모델은 RTL과 같은 동기 모델과 달리 본질적으로 분리된 동시성 개념을 제공합니다. 따라서 한 프로세스에서 다른 프로세스로 데이터를 전달하는 지연 시간이 계산 중인 데이터 스트림을 변경하지 않도록 보장되는 탄성적 또는 지연 시간 비민감 특성을 가진 분산 시스템을 쉽게 기술할 수 있습니다.
순차 코드의 세밀한 CDFG 표현에서 데이터플로우 모델을 추출할 수 있으며, 결과 데이터플로우 모델은 회로에서 직접 구현할 수 있습니다. 다만 세밀한 핸드셰이크 논리를 구현하는 데 사용되는 회로는 동등한 FSMD 모델보다 복잡한 경우가 많습니다(정적으로 스케줄링할 수 있는 설계의 경우). 더 일반적으로 데이터플로우 모델은 거친 세분성의 동기 구성 요소와 함께 사용되어 전역 비동기식-지역 동기식(GALS) 아키텍처를 구현합니다. 이 아키텍처는 대규모 구성 요소 기반 RTL 설계를 구현할 때 클록 분배와 전역 타이밍 클로저가 어려울 수 있는 대형 가속기 설계에 흔히 적합합니다.
데이터플로우 모델의 또 다른 일반적인 용도는 FireSim에서와 같이 시뮬레이션 도구로 사용하는 것입니다. FireSim은 사이클 정확 RTL 모델에서 시작하여, 시간이 모델에 명시적으로 표현되는 데이터플로우 모델을 추출합니다. 이를 통해 분산 시뮬레이션을 수행하거나 아직 RTL이 존재하지 않는 하드웨어 설계 부분의 모델을 삽입할 수 있습니다. 데이터플로우 모델을 사이클 정확하지 않은 방식으로 다시 하드웨어에 매핑하면 매우 빠른 에뮬레이션을 신속하게 구축할 수 있습니다.
IP 구성은 대부분의 FPGA 및 ASIC 설계에서 핵심 방법론이 되었으며, 작은 구성 요소로 대형 설계를 구성할 수 있게 합니다. IP 구성을 견고하게 만든 핵심은 RTL 설계에서 흔히 수행되는 신호 수준 구성이 아니라 ARM AXI와 같은 프로토콜을 사용하는 인터페이스 기반 구성에 초점을 맞춘 것입니다. 표준 인터페이스 프로토콜을 통해 설계자는 구성 요소가 내부적으로 어떻게 동작하는지의 문제와 구성 요소를 어떻게 통합하는지의 문제를 독립적으로 해결할 수 있게 되었습니다. 설계 흐름의 최하위 수준에서는 계층적 플로어플래닝과 후기 단계 인터커넥트 합성을 가능하게 하여, 배치 및 배선 중에 대형 설계가 타이밍을 충족하도록 할 수 있습니다. 또한 엔지니어링 변경 지시(ECO)라고 하는 후기 단계 변경의 영향을 캡슐화하여 불필요한 중복 검증을 피할 수 있습니다. IP 구성은 또한 예를 들어 장치 트리를 사용하는 장치 아키텍처의 선언적 기술을 기반으로 이식 가능한 운영 체제 소프트웨어와 런타임을 가능하게 합니다.
IPXact는 업계 표준 파일 형식이지만, 업계 표준 IP 정의가 부족하고 실제로는 공급업체 확장에 크게 의존하기 때문에 IPXact를 사용하는 도구는 대체로 상호 운용되지 않습니다. 또한 XML 기반이므로 IPXact는 다소 장황하고 사람이 다루기 어렵습니다. DUH는 IP 구성에도 초점을 맞춘 JSON 형식 기반의 최근 오픈 소스 노력입니다.
구축에 적합한 추상화를 갖추는 일은 중요하지만, 그러한 추상화는 단지 설계의 변환을 지원하는 수단일 뿐입니다. 관심을 가질 수 있는 흐름은 많지만, 여기서는 가장 흥미로운 흐름 몇 가지를 강조합니다.
일반적인 도구 흐름은 순차 알고리즘 코드에서 회로를 합성하는 것입니다. 많은 도구가 보통 신호 처리 응용 프로그램에 중점을 두고 이러한 유형의 도구 흐름을 구현했습니다. 이러한 흐름에서는 루프의 깊은 파이프라인화 및 병렬화 구현을 구축하는 일이 핵심 역량입니다. 전형적인 고수준 합성 흐름은 최적화된 제어 데이터플로우 그래프(CDFG)에서 시작합니다. 구현 비용을 줄이기 위해 보통 비트폭 최소화, ROM 추론, 시프트 레지스터 식별 및 하드웨어 비용 모델을 고려한 대수적 최적화와 같은 추가 하드웨어 지향 최적화를 수행합니다. 대부분의 코드에서는 메모리 분석과 최적화도 매우 중요합니다. 배열은 가능한 한 로컬 메모리에 구현되는 경우가 많고, 캐시 계층을 가능한 한 제거하도록 메모리 접근을 최적화합니다. 대역폭을 늘리기 위해 메모리 분할을 통해 메모리를 재구성하는 경우도 많습니다. 파이프라인화의 한계를 정적으로 식별하려면 정확한 포인츠투 분석과 아핀 인덱스 분석이 필요합니다. 초기 클록 사이클 경계를 식별하기 위해 연산 스케줄링을 수행합니다.
스케줄링된 설계는 FSMD 모델로 표현할 수 있습니다. 이 형태에서는 마이크로아키텍처 최적화를 식별할 수 있습니다. 연산은 동일한 하드웨어에서 공유될 수 있고, 이러한 공유로 인한 멀티플렉싱 비용을 정확하게 추정할 수 있습니다. 제어 논리 비용 역시 추정하고 최소화할 수 있습니다. 이 단계에서는 검증도 중요합니다. 원래 순차 테스트벤치로 설계를 검증하는 데 시뮬레이션을 사용할 수 있고, 생성된 제어 논리에 대한 도달 가능성 분석과 같은 부분적 정형 분석도 가능합니다. 구현 인터페이스(FIFO 인터페이스 등)를 사용하여 알고리즘 인터페이스(메모리에 대한 순차 접근 등)를 변환하는 인터페이스 합성도 이 추상화 수준에 적용할 수 있습니다.
최적화 후 FSMD 모델은 레지스터 전송 수준(RTL) 설계로 구현될 수 있습니다. 이 변환은 제어 FSM을 명시화하고 제어 논리와 데이터 경로의 구분을 흐립니다. 제어 논리와 데이터 경로 사이에서 논리 최적화가 가능하며, 이러한 최적화는 레지스터의 클록 인에이블 입력을 활용할 수 있습니다. RTL 모델은 일반적으로 Verilog 또는 VHDL로 구현되어 공급업체 도구를 사용한 구현을 가능하게 합니다. 또는 이러한 모델을 MLIR의 더 하드웨어 특화된 방언으로 추가로 낮출 수도 있습니다.
멀티코어 프로그래밍은 GPU용 CUDA와 같은 단일 프로그램 다중 데이터(SPMD) 프로그래밍을 사용하여 흔히 수행됩니다. 일반적으로 각 프로세서는 외부 메모리에 있는 대규모 데이터 집합의 일부를 로컬 메모리로 읽어 와서 로컬에서 처리한 후, 결과를 외부 메모리에 다시 기록합니다. 그 결과 프로세서 간 통신은 주로 외부 메모리를 통해 발생하며, 상당한 전력 소모와 통신 병목을 초래합니다. 온칩 메모리를 사용한 코어 간 통신도 가능하지만, 이는 전역 메모리와 별개로 명시적으로 관리해야 합니다. 명시적 데이터플로우 기반 프로그래밍은 프로세스 간 통신을 데이터 스트림으로 명시적으로 표현하므로, 통신 의도를 상태 저장소와 명시적으로 분리할 수 있습니다. 또한 데이터플로우 프로세스는 상태를 공유하지 않으므로, 데이터플로우 기반 프로그래밍은 프로그램 내 메모리 지역성을 명시적으로 기술합니다.
데이터플로우 모델에서는 많은 최적화가 가능합니다. 가장 중요하게는 융합 최적화가 데이터플로우 그래프를 조작하여 각 구성 요소가 단일 프로세서에서의 실행을 나타내는 최상위 데이터플로우 구성 요소를 생성합니다. 균형 잡힌 시스템을 생성하기 위해 이는 일반적으로 여러 목표를 가지고 수행됩니다. 주로 접근의 지역성을 고려하여 수행해야 하며, 흔히 입력을 공유하거나 파이프라인으로 데이터를 처리하는 데이터플로우 구성 요소를 병합합니다. 두 번째로 각 물리적 프로세서에서 수행되는 처리량의 균형을 맞추는 일도 중요합니다. 이는 한 프로세서가 다른 프로세서의 데이터를 기다리며 중단되는 시간을 최소화합니다. 피드백이 있는 더 복잡한 시스템에서는 피드백 경로가 신속히 완료되도록 불균형한 워크로드가 더 바람직할 수 있습니다. 데이터플로우 프로그램에 큰 세분성의 구성 요소가 존재하는 경우에는 *분할 변환도 중요할 수 있습니다. 명백히 데이터 병렬 연산은 분할/결합 쌍을 사용하여 분할할 수 있지만, 순환을 포함하는 중첩 루프로 구성된 더 복잡한 구성 요소에는 더 복잡한 분석이 필요합니다.
스트림 연산은 아키텍처에 따라 서로 다른 메커니즘을 사용하여 구현할 수 있습니다. 일부 아키텍처는 ISA에서 스트림 연산을 제공하여 물리적 스트림으로 데이터를 통신할 수 있게 합니다. 그러나 대부분의 경우 스트림 접근은 메모리 연산으로 변환됩니다. 예를 들어 범용 CPU의 스트림은 일반적으로 메모리 할당, 포인터 조작 및 스레드 간 동기화를 통해 구현되며, 프로세스 간 운영 체제 수준 파이프를 사용할 수도 있습니다. 스트림 통신은 범용 CPU와 가속기 사이에서도 발생할 수 있으며, 이 경우 스트림 통신은 DMA 하드웨어, 공유 메모리 접근 및 런타임 API 호출의 조합으로 구현됩니다. 분산 시스템에서는 예를 들어 TCP/IP를 사용하여 네트워크를 통해 스트림 통신을 구현할 수도 있습니다. 이러한 경우 공유 매체의 병목을 이해하는 일이 중요할 수 있습니다. 이 각각의 경우에서 데이터플로우 개념은 더 일반적인 프로세스/스레드/파이버 개념으로 낮아지며, 관리해야 할 중요한 아키텍처별 절충이 존재합니다. 우리는 이러한 낮춤을 명시적으로 모델링하는 편이 저수준 개념(예: 스레드 및 잠금)을 프로그래머에게 노출하는 것보다 거의 항상 프로그래머에게 더 편리하다고 주장합니다.