[베릴로그 RTL 예제] 탁구 게임기 -8편: "내 칩"-
목차:
1. RTL 베릴로그로 "탁구대" 그리기
2. 그래픽 LCD 구동 칩의 시뮬레이션 모델
3. "탁구대"의 그래픽 테스트 벤치
4. 움직이는 탁구공
5. GLCD의 버스 기능 모델
6. 탁구대, 움직이는 공 그리고 탁구채
7. 코-에뮬레이션(Co-Emulation) 검증
8. "내 칩"
----------------------------------------------------------------------------------------------------------------
오픈-소스 반도체 설계 도구가 설치된 "내 칩 디자인 킷"의 WSL 가상 디스크 이미지 [링크]
----------------------------------------------------------------------------------------------------------------
8. "내 칩"
8-1. 반도체 제조도면
"탁구 게임기"를 베릴로그로 기술하고 시뮬레이션과 에뮬레이션으로 검증까지 마쳤다. 설계를 마친 셈이다. 이제 "내 칩"을 제작의뢰 하기 위해 도면을 그릴 차례다. 설계자가 반도체라는 물질을 가지고 트랜지스터를 제조하는 과정을 모두 알 필요는 없다. 궁금 하다면 이래의 동영상을 한번 보는 것 만으로 충분하다.
반도체 제조 도면을 "레이아웃"이라 한다. 평면위에 그려놓은 반도체 물질로 만든 트랜지스터를 배치하고 배선한 도면이다. 전자회로 인쇄기판 PCB(Printed Circuit Board)의 도면과 다를바 없다. 도면의 그림 형식이 GDS 다. 간단한 "탁구 게임기"만 해도 필요한 트랜지스터 갯수가 수천에 이른다. 이들을 평면에 배치와 배선을 손수 할 일이 아니다. 프로그래밍 언어로 코딩을 해놓으면 컴파일러가 알아서 기계어로 바꿔 주고 운영체제가 이를 실행 시켜주었다. "탁구 게임기"를 베릴로그로 코딩해 놓으면 합성기가 이를 디지털 논리회로로 바꿔주고 자동 배치 배선 도구가 도면을 완성해 준다. 반도체 도면 생성은 자동화 되어있다.
제조도면 그리기는 "설계"가 아니다.
8-2. 합성
앞서 에뮬레이터 검증 편에서 "탁구 게임기"를 묘사한 베릴로그 문장을 FPGA 반도체에서 작동시켰었다. 논리회로도를 작성하지 않았었고 배치와 배선까지 자동화 도구가 해줬다. 반도체 공장에 "내 칩"을 제조해 달라고 도면을 제작하는 과정 역시 상당부분 자동화 되어있다. FPGA 에서 0.5um CMOS 공정의 전용 칩 ASIC(Application Specific IC)으로 바뀌었을 뿐이다.
"합성"은 베릴로그로 묘사된 구문을 디지털 논리회로도로 변환해주는 과정이다. "탁구 게임기"를 묘사한 베릴로그 문장을 디지털 논리회로로 변환해 주는 오픈-소스 도구는 Yosys 다. 이 합성도구를 사용하는 방법은 GNU C++ 컴파일러 사용법과 다르지 않다. 다만 제조할 공정을 지정하고 라이브러리의 경로를 맞춰줘야 하는 절차가 필요하다. C++컴파일러에서도 유사한 절차를 따라야 하지만 개발환경 도구들(아듀이노 IDE, 라즈베리 SDK 등)이 사전에 설정해 놓은 덕에 프로그래머는 수월하게 개발을 할 수 있다. 오픈-소스 반도체 설계 도구의 개발환경이 QFlow 다. 그래픽 사용자 인터페이스 GUI(Graphic User Interface) 대신 명령줄 인터페이스 CLI(Command Line Interface)다. "탁구 게임기"를 디지털 논리회로로 합성해보자. 오픈-소스 반도체 개발 환경 QFlow 는 마이크로소프트사의 비쥬얼 스튜디오처럼 친절하지 않다는 점을 미리 기억해 두자. 검증을 마친 설계 파일들을 바탕으로 합성을 수행하기로 한다. 아래 과정은 리눅스 운영체제 상에서 도구의 사용법을 보여주기 위한 예시이므로 따라할 필요는 없다. 추후 "실습"에서 다룰 것이므로 지금은 읽기와 이해에 집중하자.
작업 디렉토리 생성,
$ cd ~
$ mkdir MyChip_Work
앞서 검증한 설계 파일들을 임의의 디렉토리에 복사해 온다.
$ cd MyChip_Work
$ cp -r ~/ETRI050_DesignKit/Projects/RTL/pong_SbS/07_Co-Emulation .
예제 디렉토리로 이동,
$ cd 07_Co-Emulation
디렉토리 구조와 설계 파일들을 살펴본다.
$ tree
.
├── /emulation
│ ├── Epong_SbS.h
│ ├── Makefile
│ ├── pong_SbS_wrapper.v
│ ├── /PSCE-MI
│ │ ├── /Epong_SbS
│ │ │ ├── Epong_SbS.ino
│ │ │ ├── PinMap_A7_100T.h
│ │ │ ├── PinMap_TANG_25K.h
│ │ │ ├── PSCE_APIs.cpp
│ │ │ ├── PSCE_APIs.h
│ │ │ ├── PSCE_Config.h
│ │ │ └── PSCE_Splash.h
│ │ └── Makefile
│ └── /PSCE-TRANS
│ └── /Altera_Cmd
│ ├── Makefile
│ └── pong_SbS_wrapper.tcl
├── /pong_SbS
│ └── pong_SbS.v
└── /simulation
├── Makefile
├── sc_glcd128x64_TLM.cpp
├── sc_glcd128x64_TLM.h
├── sc_main.cpp
├── sc_pong_SbS_TB.cpp
├── sc_pong_SbS_TB.gtkw
├── sc_pong_SbS_TB.h
└── Vpong_SbS.gtkw
8 directories, 22 files
"탁구 게임기"의 베릴로그는 pong_SbS/pong_SbS.v 다. 시뮬레이션과 에뮬레이션 작업 디렉토리를 별도로 만들었던 것처럼 "내 칩" 작업 디렉토리 ETRI050 를 만들고 이동한다.
$ mkdir ETRI050
$ cd ETRI050
합성 하면서 생성되는 각종 중간 파일들을 저장할 디렉토리를 만들어 준다. QFlow 환경에서 반드시 필요한 하위 디렉토리는 layout, log, synthesis, source 다.
$ mkdir layout
$ mkdir log
$ mkdir synthesis
"탁구 게임기"의 베릴로그 디렉토리를 source 로 심볼(소프트)링크 한다.
$ ln -s ../pong_SbS ./source
$ ll
total 8
drwxr-xr-x 2 mychip ...... layout
drwxr-xr-x 2 mychip ...... log
lrwxrwxrwx 1 mychip ...... source -> ../pong_SbS
drwxr-xr-x 2 mychip ...... synthesis
QFlow 프로젝트 파일을 만들기 위해 GUI 모드로 실행,
$ qflow gui
합성 목표 공정을 etri050 으로 변경,
이어 "Run" 버튼을 눌러 "Okay"를 확인한다.
QFlow 를 사용할 환경이 준비됐다. 이후 과정은 모두 명령줄로 실행될 것이다. QFlow 는 종료한다. 디렉토리 목록을 보면 project_vars.sh 가 생성된 것을 확인한다.
$ ll
total 24
drwxr-xr-x 2 mychip mychip 4096 Mar 22 11:37 layout
drwxr-xr-x 2 mychip mychip 4096 Mar 22 11:37 log
lrwxrwxrwx 1 mychip mychip 11 Mar 22 11:17 source -> ../pong_SbS
drwxr-xr-x 2 mychip mychip 4096 Mar 22 11:18 synthesis
-rw-r--r-- 1 mychip mychip 1733 Mar 22 11:37 project_vars.sh
-rwxr--r-- 1 mychip mychip 1461 Mar 22 11:37 qflow_exec.sh
-rw-r--r-- 1 mychip mychip 683 Mar 22 11:37 qflow_vars.sh
합성 명령을 주고 QFlow 실행,
$ qflow synthesize -T etri050 pong_SbS
화면에 메시지가 지나간다. log 디렉토리에 합성기의 메시지들이 기록된다. 노련한 설계자는 도구가 보여주는 각종 메시지를 이해한다. 실력은 이 기록 파일들을 이해하고 적절한 대응을 할줄 아는가에 달렸다. 최소한 합성 오류가 없다는 것부터 확인하자.
Done.
합성 로그 파일 log/synth.log 에서 기초적으로 확인할 사항은 "내 칩"의 디지털 회로 구성이다. 회로의 규모를 파악할 수 있다.
Qflow synthesis logfile created on Sun Mar 22 11:46:32 AM KST 2026
Running yosys for verilog parsing and synthesis
yosys -s pong_SbS.ys
/------------------------------------------------------------------\
| yosys -- Yosys Open SYnthesis Suite |
| Copyright (C) 2012 - 2026 Claire Xenia Wolf<claire@yosyshq.com>|
| Distributed under an ISC-like license, type "license" to see |
\------------------------------------------------------------------/
Yosys 0.62+9 (git sha1 1717fa018, clang++ 18.1.3 -fPIC -O3)
......
12. Printing statistics.
=== pong_SbS ===
+----------Local Count, excluding submodules.
|
472 wires
511 wire bits
472 public wires
511 public wire bits
9 ports
20 port bits
506 cells
17 AND2X2
24 AOI21X1
15 AOI22X1
15 BUFX2
38 DFFSR
66 INVX1
1 MUX2X1
105 NAND2X1
51 NAND3X1
71 NOR2X1
1 NOR3X1
83 OAI21X1
8 OAI22X1
11 OR2X2
Generating RTL verilog and SPICE netlist file in directory
/home/mychip/MyChip_Work/07_Co-Emulation/ETRI050/synthesis
Files:
Verilog: .../ETRI050/synthesis/pong_SbS.rtl.v
Verilog: .../ETRI050/synthesis/pong_SbS.rtlnopwr.v
Verilog: .../ETRI050/synthesis/pong_SbS.rtlbb.v
Spice: .../ETRI050/synthesis/pong_SbS.spc
Reading liberty netlist /usr/local/share/qflow/tech/etri050/etri05_stdcells.lib
Reading spice netlist pong_SbS.spc
Writing xspice netlist pong_SbS.xspice
Writing xspice file
Done.
8-2. 합성 후 시뮬레이션
합성기의 오류는 없을까? 베릴로그 구문에서 디지털 회로로 변환(합성)해 주는 Yosys 합성기는 소프트웨어(응용 프로그램)이다. 내부 '버그'로 부터 자유롭지 않다. 게다가 합성의 결과로 얻은 네트리스트는 소프트웨어에서 디지털 회로의 하드웨어로 옮겨 왔으므로 '회로 경로' 상 시간지연을 담고 있다. 클럭 엣지의 시간조건 오류(바이얼레이션, clock timing violation, setup-hold violation)도 살펴봐야 한다. 변환이 기능적으로 이상이 없을지, 회로상 바이얼레이션은 없을지 확인해보기 위해 합성 후 네트리스트 시뮬레이션을 실시한다.
합성 후 시뮬레이션 용도의 네트리스트 베릴로그로는 synthesis/pong_SbS_mapped.v 다. 무려 3천줄이 넘는다. 합성 로그에서 봤던 506개의 셀들과 450여개에 이르는 배선(wire)들이 기술되었다. (*...*) 는 합성기 내부 정보를 표시하는데 디지털 셀들이 베릴로그의 어느 문장에서 도출되었는지 표시하고 있다. 불필요하게 생성된 부분은 없는지 확인 할 수 있다.
/* Generated by Yosys 0.62+9 (git sha1 1717fa018, ... -fPIC -O3) */
(* top = 1 *)
(* src = ".../pong_SbS.v:9.1-169.10" *)
module pong_SbS(clk, reset, x_pos, y_pos, pixel, p_tick, busy, up, down);
(* src = ".../pong_SbS.v:10.17-10.20" *)
input clk;
wire clk;
(* src = ".../pong_SbS.v:11.17-11.22" *)
input reset;
wire reset;
(* src = ".../pong_SbS.v:12.17-12.22" *)
output [6:0] x_pos;
wire [6:0] x_pos;
(* src = ".../pong_SbS.v:13.17-13.22" *)
output [5:0] y_pos;
wire [5:0] y_pos;
(* src = ".../pong_SbS.v:14.17-14.22" *)
output pixel;
wire pixel;
......
(* onehot = 32'd1 *)
wire [1:0] State;
wire _0_;
wire _100_;
wire _101_;
............
wire _99_;
wire _9_;
(* src = "pong_SbS.v:136.17-136.23" *)
wire [5:0] paddle;
(* src = "....../pong_SbS.v:25.17-25.23" *)
wire v_sync;
(* src = "....../pong_SbS.v:75.15-75.21" *)
wire [5:0] y_ball;
(* keep = 32'd1 *)
BUFX2 _456_ (
.A(_454_[6]),
.Y(x_pos[6])
);
......
(* src = ".../pong_SbS.v:76.5-93.8" *)
DFFSR _960_ (
.CLK(clk),
.D(_370_),
.Q(y_ball[4]),
.R(1'h1),
.S(_388_)
);
DFFSR _961_ (
.CLK(clk),
.D(busy),
.Q(State[0]),
.R(1'h1),
.S(_388_)
);
endmodule
합성 후 네트리스트의 하위 모듈들(BUFX2, DFFSR 등)은 모두 표준 셀이다. 이 표준 셀들의 기능적, 시간적 모델은 디자인 킷으로 제공된다. 표준 셀의 타이밍 모델과 네트리스트를 묶어 시뮬레이션을 실시한다. 합성 후 네트리스트 시뮬레이션 또한 검증의 일관성을 유지해야 한다. 기능 시뮬레이션에 사용했던 SystemC 테스트 벤치를 네트리스트 타이밍 시뮬레이션에서도 동일하게 적용한다. 베릴로그의 VPI(Verilog Procedural Interface)를 통하면 C 언어로 작성된 함수를 호출할 수 있다. 베릴레이터가 포용하는 범위는 제한적이다. 베릴로그로 높은 추상화 수준에서 타이밍 모델링할 수 있는 사용자 지정 프리미티브(UDP, User Defined Primitives)를 지원하지 않는다. 따라서 네트리스트 타이밍 시뮬레이션은 다른 오픈-소스 베릴로그 시뮬레이터 이카루스베릴로그 iVerilog를 사용한다.
VPI(PLI2.0) 표준은 매우 방대하다. 객체 표현 추상성이 낮은 C 언어에 기초하고 있어서 하드웨어를 기술하려면 번거로운 절차를 따라야 한다. 운용 방식은 기본적으로 사건과 콜-백 함수의 조합이다. GNU C++ 컴파일러의 넓은 호한성 덕에 SystemC의 C++를 VPI에 연결 시킬 수 있다. 방대한 VPI에 대한 설명은 다음으로 미루고 준비된 VPI 루틴을 활용하여 네트리스트 타이밍 시뮬레이션을 수행해본다.
ETRI050 작업 디렉토리에 하위 시뮬레이션 디렉토리를 만든다.
$ pwd
~/MyChip_Work/07_Co-Emulation/ETRI050
$ mkdir simulation
$ cd simulation
미리 만들어 놓은 VPI 소스 코드 복사,
$ pwd
~/MyChip_Work/07_Co-Emulation/ETRI050/simulation
$ cp ~/ETRI050_DesignKit/Projects/RTL/pong_SbS/08_ETRI050/ETRI050/simulation/*.* .
$ ls -l
total 36
-rw-r--r-- 1 mychip ...... pong_SbS_TB.v
-rw-r--r-- 1 mychip ...... sc_pong_SbS_TB.h
-rw-r--r-- 1 mychip ...... vpi_pong_SbS_tb.cpp
-rw-r--r-- 1 mychip ...... vpi_pong_SbS_tb_exports.h
-rw-r--r-- 1 mychip ...... vpi_pong_SbS_tb_ports.h
-rw-r--r-- 1 mychip ...... vpi_stub.cpp
SystemC 테스트벤치를 재사용하기 위해 기능 시뮬레이션에 사용되었던 파일들을 현재 디렉토리로 심볼 링크,
$ ln -s ../../simulation/sc_pong_SbS_TB.cpp ./sc_pong_SbS_TB.cpp
$ ln -s ../../simulation/sc_glcd128x64_TLM.cpp ./sc_glcd128x64_TLM.cpp
$ ln -s ../../simulation/sc_glcd128x64_TLM.h ./sc_glcd128x64_TLM.h
VPI 모듈을 빌드한다. 만들어지는 바이너리 vpi_stub,vpi 는 SystemC 테스트벤치와 함께 링크되는 동적 라이브러리(쉐어드 오브젝트, shared object)다.
$ g++ -DVPI_SIM \
-I/usr/local/include/iverilog \
-I/opt/systemc/include -I.. \
-L/opt/systemc/lib \
-g -fPIC \
-o vpi_stub.vpi \
-shared -latomic -lsystemc -lgsl -lSDL2 \
./vpi_stub.cpp \
./sc_glcd128x64_TLM.cpp \
./vpi_pong_SbS_tb.cpp \
./sc_pong_SbS_TB.cpp
이어 베릴로그 DUT와 테스트벤치를 묶어 시뮬레이터를 빌드한다. 표준셀의 타이밍 모델은 khu_etri05_stdcells.v에 기술되었다.
$ iverilog -g2005-sv -Tmin -gspecify \
-o pong_SbS_TB \
pong_SbS_TB.v \
~/ETRI050_DesignKit/digital_ETRI/khu_etri05_stdcells.v \
../synthesis/pong_SbS_mapped.v
앞서 빌드한 VPI 라이브러리와 함께 시뮬레이터를 실행 시켜보자.
$ vvp -M. -mvpi_stub pong_SbS_TB -v
vpi_stub:`./vpi_stub.vpi' failed to open using dlopen() because:
libsystemc.so.3.0: cannot open shared object file
pong_SbS_TB.v:59: Error: System task/function
$sc_pong_SbS_tb() is not defined by any module.
pong_SbS_TB: Program not runnable, 1 errors.
쉐어드 오브젝트 libsystemc.so.3.0 를 찾지 못해서 VPI 함수 $sc_pong_SbS_tb()를 호출할 수 없다고 한다. 리눅스 파일 시스템에서 동적 링크 SystemC의 쉐어드 오브젝트 파일의 위치를 찾아보자.
$ find /opt -name "libsystemc.so.3.0"
/opt/systemc/lib/libsystemc.so.3.0
리눅스 배쉬 쉘의 변수 LD_LIBRARY_PATH 는 동적 링크 라이브러리의 탐색 경로를 지정한다. 위에서 찾은 경로를 환경변수에 넣고 시뮬레이션을 실행 한다.
$ LD_LIBRARY_PATH=/opt/systemc/lib
$ vvp -M. -mvpi_stub pong_SbS_TB -v
베릴로그 기능 시뮬레이션에 비해 실행이 매우 느리다. 네트리스트 베릴로그에 500여개의 하위 모듈과 네트들이 모두 사건을 발생하기 때문이다. 타이밍 시뮬레이션으로 얻은 파형을 관찰해보자.
$ gtkwave pong_SbS_TB.vcd
시뮬레이션이 시작되기 직전에 모든 출력 신호들에 붉은 색의 '알수없음(unknown)'이다.
리셋 reset 입력이 주어지면서 '알수없음'이 더이상 지속되지 않고 있다. '알수없음'이 가장 길게 지속되는 출력이 pixel이다.
탁구대, 공 그리고 패들을 그리는 픽셀 값이 조합회로 였기 때문이다.
// Table --------------------------------------------------------
wire pixel_table = ((x_pos>5) && (x_pos<15))? 1:0;
// Ball ---------------------------------------------------------
reg pixel_ball;
always @*
if ((x_ball<=x_pos) && ((x_ball+7)>=x_pos) &&
(y_ball<=y_pos) && ((y_ball+7)>=y_pos))
pixel_ball = rom_data[rom_bit];
else
pixel_ball = 0;
// Paddle -------------------------------------------------------
wire pixel_paddle;
assign pixel_paddle = ((x_pos>122) && (y_pos>paddle) && (y_pos<(paddle+20)))? 1:0;
// Pixel --------------------------------------------------------
assign pixel = (pixel_table ^ pixel_ball) | pixel_paddle;
클럭 clk 의 상승 엣지 부분을 확대해 보면 출력 신호들이 저마다 지연을 반영하고 있는 것을 확인할 수 있다.
만일 타이밍 시뮬레이션에서 오류가 발생한다면 디지털 파형은 중요한 단서를 제공한다. 베릴로그 설계에서 플립플롭이 모두 비동기식 리셋을 가졌음에도 불구하고 초기 "알수없음"으로 시작하는 이유를 찾아보라.
8-3. 표준 셀 배치와 배선
시뮬레이터를 빌드하고 실행시켜 보는데 명령줄이 너무나 길다.

댓글 없음:
댓글 쓰기