SystemC와 HLS

몇 가지 실전 상황에 대해서 잠시 실험해 본 바를 정리한다. 아주 원리적이고 지극히 당연한 이야기인데 복습할 겸 써보는 것이다.

HDL을 구동하니 한가지 확실한 것은 thread를 잘게 쪼개서 state를 구분하고 그 state가 clock에 따라 계속 카운트되면서 전환이 되면 거기에 맞춰서 mux가 데이터들을 data path에 흘려주는 식의 결과로 나오게 된다.

즉 HDL의 모델이라는 게 여러가지 연산 로직들이 연결된 data path와 그것들을 컨트롤하는 control path로 구분되어 생각되어지듯, 같은 개념으로 C++ 프로그램을 분석해서 연산로직 무더기를 주어진 sequence에 따라서 연결해주면 데이터가 그 연산로직을 따라 흘러가서 최종적인 결과에 도달하게 된다는 것이다.

EDA 회사에서 하는 그 일반적인 알고리즘이 여기에도 그대로 적용된 것이라고 본다. 그래서 빌드가 시작되면 필요한 자원을 모두 찾아내는 일을 시작하고, 그때마다 그 자원들을 합성한다. 합성이란 게 덧셈기 곱셈기를 스펙 (입출력)에 맞춰 만들어낸다는 것이다. 그리고 그 자원들을 연결해내는 mux를 설계하고 주어진 state에 따라 그 mux의 연결을 이리저리 바꿔서 원하는 결과를 얻게 만든다.

최종 합성 결과를 보면 엄청난 크기의 mux가 붙고 이런 저런 연산에 사용되는 add/sub/mul이 생성되어있다. 따라서, 이 복잡도를 줄이려면 한방에 계산할 걸 loop로 계산하게 한다든가 (더 많은 시간이 걸리더라도 적은 자원을 쓰게) 작업 자체의 복잡도를 어떻게든 줄여내는 노력이 필요한 것이다. 컴퓨터가 해줄 수 있는 일은 사용자의 코드 그대로 최대한 빨리 결과를 얻게끔 연산자원을 추려내고 그 연산자원을 시간에 맞춰 스케줄링 해주는 일 뿐이다.

같은 얘길 3-4번 중복하고 있지만 그것 말고 별 다른 것은 없다. 사람이 할 수 있는 일은 area가 크게 나오는 entity가 어디에서 쓰이는지 결과물의 .v를 보고 확인해서 하나씩 제거해나가는 방법 정도가 맞다고 본다. 대개 알고리즘이 확립되어있기 전에 HLS하러 오진 않기 때문이니까 이 단계에서 알고리즘을 새로 바꿔본다 하는 것은 처음부터 다시 시작한다는 것이니까 말이다.

결과물의 .v를 보면 그래도 오랜 동안의 노우하우 때문인지 생각보다 제작자의 의도대로 잘 합성이 되어있고 엉뚱하게 해석해서 말도 안되는 로직을 갖다붙였다거나 하는 것은 거의 보이지 않는다. 그러니까 툴이 비쌀만 하다는 말도 되겠다.

다만 그 결과물은 사람이 읽기 어려운 상태의 것이 되어버리는 것이 개념적인 연산의 흐름에 맞춰서 작성되어져있지 않고 컴퓨터가 구분한 전체적인 로직의 상태에 따라 구분해서 같은 로직도 이렇게 돌았다가 저렇게 돌았다 하는 식으로 되어있어서 switch/case/case 이런 식으로 되어있고 state도 내용을 알 수 없는 번호들로 구분되어있기 때문에 사실상 .v 수준으로 내려가면 디버깅은 불가하다고 봐야 한다.

더 엄밀히 얘기하자면 그 옛날 synopsys에서 함수를 써서 만들었을 때의 그 황당한 결과 (스스로 이런 저런 라이브러리를 불러내서 비인간적으로 설계해버리는)와 비슷하다고 해야할 것 같다. 이런 꼴을 보느니 그냥 .v로 직접 진행하는 게 맞겠다 싶은 생각이 들 수 밖에 없다. 세상이 많이 좋아지긴 했지만 말이다.