This blog is about VLSI design using Open-Source EDA Tools, including SystemC, iVerilog, Yosys, Auto-P&R, NetGen and Magic.
If you're about to begin IC design, Start here;
컴퓨팅 언어를 써서 디지털 반도체 회로를 설계하려는 목표가 있다고 하자. 이 목표를 위해 학습을 하지만 한학기(대개 16주)동안 어떤 컴퓨팅 언어를 배우는 과정은 지루하기 이를데 없다. 게다가 학습을 평가한다면서 시험을 치뤄 점수를 메긴다 하니 학습자의 입장에서 매우 피곤할 노릇이다. 학점을 채우기보다 궁금증의 비중이 더 크다면 차라리 유튜브 동영상을 찾아보길 권한다. 유튜브에 수많은 동영상 강좌가 넘쳐난다. 게중에 무리하게 학습 시간을 요구하는 경우나 알아듣기 어려운 외국어(영어로 말하지만 영어일까 싶은) 강좌는 배제하고 간단 명료한 동영상을 선별하여 소개해 본다. 이 동영상의 내용을 이해 했다면 컴퓨팅 언어를 사용한 반도체 설계에 필요한 최소 수준을 갖췄다고 봐도 좋다. 늘 그렇듣 연습 만이 살길이다.
1. 선수지식
강좌를 보기 전에 현재 갖춰야할 요건이라면 아래와 같다.
- 논리회로는 조합회로와 순차회로가 있으며 이 둘의 차이를 구분할 줄 안다. - 카운터와 타이머 그리고 유한 상태머신의 용도를 이해한다. - C 언어로 "Hello World!"라는 문장을 출력해 봤다. - C++ 언어의 크래스(구조체와 소속함수들)와 템플릿을 안다.
2. 베릴로그 언어
베릴로그 언어를 배우는 목적을 아래와 같이 정하자.
레지스터 전송 수준(RTL, Register-Transfer Level)에서 디지털 회로를 묘사할 수 있다.
* 이유는 알 수 없지만 앞서 링크한 유튜브 동영상이 막혀 버려서 좀더 단순한 베릴로그 기초 동영상으로 대체 한다. 앞의 동영상에서 사용된 인쇄물은 아래 링크에서 내려받을 수 있다.
두말할 나위도 없이 SystemC는 언어가 아니다. C++의 크래스 라이브러리일 뿐이라고 한다. 그렇다면 동영상을 보기 전에 먼저 생각해 보자. C++ 라는 소프트웨어 작성용 언어로 하드웨어의 행동을 기술 할 수 있을까? 반도체 회로(하드웨어)를 다룬다고 하면서 C++ 는 왜 끌어들인걸까?
이 강좌는 10~20분 짜리 동영상 6편으로 구성되었다. 처음 세편 정도는 주의깊게 보고 나머지는 틈날 때 봐도 좋다.
Learn SystemC: Introduction
Learn SystemC: SC_CTHREAD, Clocked Threads
Learn SystemC: Testbenches
4. 하드웨어 설계 언어
컴퓨팅 언어들은 저마다 폭넓은 추상화 수준(Abstraction Level)을 수용한다고 주장한다. 하드웨어 기술 언어(HDL, Hardware Description Language)도 다르지 않다. 높은 수준의 테스트 벤치를 작성 한다며 온갖 기법들을 끌어들여 자랑하는 경우를 보게 되는데 그러지 말자. 베릴로그는 디지털 회로로 합성해 내기위한 목적에 충실하자. SystemC는 언어가 아니라 C++의 크래스 라이브러리일 뿐이라고 한다. 하드웨어를 설계한다면서 소프트웨어 언어를 동원한 이유에 대하여 생각해 보자.
- 반도체 설계/검증 언어 [바로가기] - SystemC는 여전히 최신 시스템 설계 방법론 일까? [바로가기]
SystemC는 하드웨어의 행위(hardware behavior)를 묘사하기 위한 크래스(C++ class)와 동시실행 시뮬레이터(concurrency simulator)가 포함된 C++의 라이브러리다. 레지스터 전송수준(RTL)은 물론 트랜잭션 수준(TLM, Transaction Level Modeling)을 비롯하여 시스템 수준까지 매우 넓은 추상화 수준(abstraction level)을 지원한다. 합성가능(synthesizable)한 RTL의 CPU에서부터 운영체제 그리고 응용 프로그램까지 설계 할 수 있다는 의미다. 별도의 언어체계가 아닌 C++ 그 자체이기 때문에 컴퓨터로 할 수 있는 모든 것이 가능하다[SystemC는 최신방법론 인가?]. 물론 설계자의 상상력에 달려 있긴 하다. SystemC의 활용의 예로 시뮬레이션과 에뮬레이션을 함께 수행하는 기법(Co-Simulation/Co-Emulation)을 소개한다. 에뮬레이션은 컴퓨터 시스템의 주변장치로 연결된 하드웨어를 시뮬레이터로 끌어들인다. 이더(Ethernet)으로 연결된 다수의 컴퓨터를 동원하여 대규모 병렬 컴퓨팅을 구현할 수 있다. 주변장치와 컴퓨터 시스템 사이의 인터페이스 병목을 해결하면 FPGA는 물론 GPU 활용 시뮬레이션 가속기(HDL simulation accelerator)로 활용할 수 있다.
앞서 다룬 FIR8 알고리즘[바로가기]을 아듀이노 보드(Arnuino)에 구현하고 이를 에뮬레이션 하드웨어로 사용하는 예를 살펴본다. 컴퓨터와 아듀이노 보드 사이의 USB 인터페이스가 고속이라고 할 수 없는 직렬통신(serial communication)이기에 가속기로서 의미는 없지만 SystemC의 가능성을 엿볼 수 있을 것이다. 향후 MPW로 제공받은 칩 테스트의 용도로 쓰일 수 있다. 시뮬레이션과 에뮬레이션을 엮어 동시에 수행하게 되므로 테스트벤치를 재사용 할 수 있다.
2. WSL에서 USB 장치 사용법
리눅스 운영체제에서 직렬 포트는 터미널의 용도로 널리 사용되어 왔기에 주변장치로 매우 안정된 작동을 보여준다. WSL 서비스의 가상 머신(virtual machine)으로 리눅스가 설치되었다면 윈도우즈 운영체제의 장치 드라이버를 연결해 주어야 한다.
윈도우즈 파워-쉘(Power-Shell)을 관리자 권한으로 열어 가상 머신 리눅스와 공유가능한 USB 장치의 목록을 확인한다.
PS C:\> usbipd list
Connected: BUSID VID:PID DEVICE STATE 2-8 045e:09c0 USB Input Device Not shared 2-10 8087:0026 Intel(R) Wireless Bluetooth(R) Not shared 4-2 343c:0000 USB Type-C Digital AV Adapter Not shared 4-3 2a03:0043 USB Serial Device (COM3) Not shared
이 목록은 컴퓨터에 연결된 장치에 따라 다르다. 나열된 장치들이 모두 공유되어 있지 않다. 아듀이노 보드의 USB 장치를 찾아 공유 시킨다. 아듀이노 보드는 USB의 직렬 포트로 연결된다. 위의 목록에서 "USB Serial Device (COM3)" 다. 윈도우즈에서 아듀이노 IDE를 설치했다면 "Arduino Uno (COM3)"로 목록에 표시될 것이다. 아듀이노 보드의 BUSID를 WSL에 공유시키는 명령은 아래와 같다.
PS C:\> usbipd bind --busid 4-3
공유가 이뤄 졌는지 확인해보자.
PS C:\> usbipd list
Connected: BUSID VID:PID DEVICE STATE 2-8 045e:09c0 USB Input Device Not shared 2-10 8087:0026 Intel(R) Wireless Bluetooth(R) Not shared 4-2 343c:0000 USB Type-C Digital AV Adapter Not shared 4-3 2a03:0043 USB Serial Device (COM3) Shared
공유된 USB 장치를 WSL에 연결(attach)시킨다. 장치 식별번호 busid 에 유의한다. 파워-쉘이 관리자 권한이어야 한다.
PS C:\> usbipd attach --wsl --busid 4-3
usbipd: info: Using WSL distribution 'Ubuntu-20.04' to attach; the device will be available in all WSL 2 distributions. usbipd: info: Using IP address 172.17.128.1 to reach the host.
리눅스 터미널을 열어 lsusb 명령으로 USB 장치들의 목록을 확인해 보자.
~$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 002: ID 2a03:0043 dog hunter AG Arduino Uno Rev3 Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
나열된 목록에 윈도우즈에서 연결시킨 아듀이노 보드가 보인다.
3. 아듀이노 개발환경 설치
아듀이노 개발환경(IDE, integrated development environment)은 윈도우즈와 리눅스용이 있다. 아래 링크에서 64비트 리눅스용 압축 파일을 내려받는다.
리눅스의 실행 파일 경로에 바로가기가 생성되었으므로 어느 위치에서도 아듀이노 IDE를 실행할 수 있다.
$ cd $ cd ETRI-0.5u-CMOS-MPW-DK-Example--FIR8/0_algorithm/sc_emulated/ $ arduino-ide fir8_ino
아두이노 보드가 리눅스의 장치 /dev/ttyACM0 에 연결된 것을 확인한다.
4. 아듀이노 보드에 FIR8 알고리즘 구현
아듀이노 보드는 8비트 마이크로 프로세서 ATMEGA(AVR)을 내장하고 있다. 프로그램 개발도구로 전용 C/C++ 컴파일러를 사용한다. 이 도구는 오픈-소스로서 표준 C/C++다. 따라서 앞 장에서 다뤘던 언-타임드 FIR8 알고리즘 fir8.cpp 을 변경 없이 구현 할 수 있다. 아듀이노 에뮬레이션 코드는 아래와 같다.
아듀이노는 단순히 마이크로 프로세서 활용 보드를 넘어 개방형 시스템 개발 도구로 성공적인 모습을 보여준다. 수없이 많은 개발자(취미가들도 상당부분을 차지한다)들이 다양한 입출력 장치 프로그램의 라이브러리를 제공하여 아듀이노 개발환경을 풍요롭게 하고있다. USB 직렬 포트 구동 라이브러리도 그중 하나다. 사용법도 매우 단순하다. 설정 함수에서 보오 율(baud rate)을 설정하고 호스트 컴퓨터의 연결을 시도한다.
void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect(native USB port only) }
establishContact(); // send a byte to establish contact
아듀이노 보드는 별도의 운영체제를 갖지 않으므로 펌웨어 내부에서 무한반복하며 시리얼 포트의 입력을 기다린다.
void loop() { if (Serial.available() > 0) { digitalWrite(LED_BUILTIN, LOW); incomingByte = Serial.read(); x = (uint8_t)incomingByte;
fir(&y, x);
for (int i=0; i<FILTER_TAP_NUM; i++) { outingByte = (int)(shift_reg[i]); Serial.write(outingByte); // Send shift register X }
outingByte = (int)(y & 0x00FF); Serial.write(outingByte); // LSB of y outingByte = (int)((y>>8) & 0x00FF); Serial.write(outingByte); // MSB of y
digitalWrite(LED_BUILTIN, HIGH); } }
직렬 포트를 통해 8비트 입력를 받아 필터 함수 fir(*y, x)를 호출한다. 계산 결과 y는 16비트 이므로 두번에 걸쳐 전송한다. 디버깅을 위해 쉬프트 레지스터 격인 shift_reg[]를 결과 이전에 전송하고 있다.
5. 에뮬레이션 프로토콜 정의
호스트에서 실행중인 SystemC 테스트 벤치와 아듀이노 에뮬레이션 보드 사이의 프로토콜(protocol, 통신규칙)을 정의한다. 이 통신규칙은 설계물에 따라 입출력 신호의 비트 폭이 다르다. 직렬통신은 8비트 단위의 데이터 전송이므로 비트 단위로 조합과(또는) 분할로 에뮬레이션 될 설계물(함수)에 맞춰져야 한다. 통신이 발생하는 시점은 전적으로 호스트 쪽의 타임드 테스트벤치 모델에 의해 결정된다. 에뮬레이션을 위한 fir8 의 SystemC 모듈은 아래와 같다.
모듈이 사례화(instantiate)되면 크래스의 구성자가 최초 실행된다. 이 구성자 내에서 운영체제의 직렬 포트 장치 구동기를 초기화 하고 에뮬레이션 보드와 연결을 시도한다. 리눅스 운영체제는 주변장치를 파일로 취급하므로 표준 입출력 장치 stdio.h 의 open(), read(), write()로 접근한다. 연결 시도 규칙은 에뮬레이션 보드에서 'A'를 보내오면 호스트는 이를 반송하여 연결을 확인토록 하였다. WSL 리눅스에서 아듀이노 보드가 연결된 장치명은 /dev/ttyACM0 였다. 이 장치를 입출력이 가능하도록 설정하고 보오율은 아듀이노 보드 측의 펌웨어 설정과 동일한 9600 이다.
에뮬레이터는 직렬 포트로 전송된 x 를 받아 fir()을 호출하고 필터 계산을 수행한 뒤 그 결과를 호스트로 전송한다. 계산결과를 전송하기 전에 디버깅 목적으로 8개 쉬프트 레지스터 값을 먼저 내보낸다. 필터 함수 fir()의 계산 결과 y 는 16비트 이므로 2번에 걸쳐 전송된다. 이때 하위 바이트 먼저 전송하기로 하였다.
호스트의 SystemC 에뮬레이터 구동 모듈은 직렬 포트를 통해 전송된 8비트 데이터를 받아 이를 16비트로 재조립하여 Yout로 내보낸다. SystemC 시뮬레이터는 비로서 제어권을 받아 시간을 진행 시킨다.
에뮬레이션에서는 하위 SystemC 모듈로 구성 하였던 타임드 모형 sc_fir8.h 을 장치 구동 쓰레드 함수로 대체되었을 뿐 외형은 변함이 없다. 따라서 타임드 알고리즘의 시험에 사용했던 테스트 벤치를 변경없이 적용 한다. 시뮬레이션과 에뮬레이션이 병행된 검증용 메이크 파일은 다음과 같다.