SystemVerilog와 C 사이의 data exchange는 DPI-C interface를 통해 이루어진다.
이번 게시글에서는 SV가 C 함수를 호출한다는 것 자체보다, SV에서 C로 함수 호출시 argument를 건네줌으로써 data를 건네줄 수 있다는 데에 초점을 맞추어서 본다!!
Data Mapping between SV and C
대부분의 SystemVerilog Data Type들은 C언어와 직접 대응되는 data type이 있는 반면,
그렇지 않은 데이터 타입들(4 state variable이나 array)들은 DPI-C나 API에 define된 특정 type을 필요로 한다.. ( 기존의 SystemVerilog Data Type이 아니라, user-defined? DPI에서 새롭게 정의된?)
SystemVerilog Type 과 C data type 대응 | |
SV | C |
byte | char |
shortint | short int |
int | int |
longint | long int |
real | double |
string | char* |
string[n] | char* |
chandle | char* |
SystemVerilog Type 과 DPI-defined type 대응 | |
SV | C |
bit | svBit |
bit[n:0] | svBitVecVal |
logic | svLogic |
reg | svLogic |
logic[n:0] | svLogicVecVal* |
reg[n:0] | svLogicVecVal* |
int[] | svOpenArrayHandle |
byte[] | svOpenArrayHandle |
shortint[] | svOpenArrayHandle |
longint[] | svOpenArrayHandle |
real[] | svOpenArrayHandle |
함수 호출시 argument(인자)를 전달하는 방법 두 가지.
SystemVerilog와 C에서는 함수 호출시 argument를 전달하는 방법엔 2 가지가 있다!
1. pass by value (call by value)
caller(함수 호출한 애)가 값을 전달하면, callee(호출받은 함수)는 값을 함수 내 variable에 copy하여 사용한다.
따라서 그 variable의 값을 변경시켜도, 함수 범위 밖에서의 값은 변하지 않는다.
2. pass by reference (call by reference)
caller(함수 호출한 애)가 전달하려는 변수의 주소값(reference)을 전달하고 , callee(호출 받은 함수)는 값을 함수 내 포인터 variable에 그 data의 reference를 담는다. 결과적으로 함수 내 arg variable은 caller가 전달한 data에(바로 그 주소에) 직접 접근하게 된다. 따라서 함수에서 포인터 변수가 참조하는 값을 변경하면, 함수 범위 밖에서도 값이 변해있다.
SystemVerilog에서 DPI호출 시 pass by value인지, pass by reference인지는 argument direction에 의해서 결정된다.
1. pass by value (call by value)
// SV에서 call by value로 C함수 import할 때,
function void f1(int a); // a는 input이지만 direction이 implicit.
function void f2(input int a); // explit direction mentioned
이처럼 system verilog는 argument의 direction이 input 이면, call by value이다. (값의 사본(copy)이 전달된다.)
// C에서 pass by value로 선언된 함수
void f1 (int a);
그냥 C 문법과 사실 별 다를 바 없음.
2. pass by reference (call by reference)
// SV에서 Pass By Reference C함수를 import할 때!
function void f1(output int a);
// direction of a is output!!
이처럼 system verilog는 argument의 direction이 output 이면, call by reference이다. (값의 주솟값(reference)이 직접 전달된다.)
// C에서 Pass By Reference 함수를 선언할 때!
void f1(int * a);
일반적인 C에서 Call by reference함수를 선언할 때와 동일하다.
a라는 pointer변수로 받은 reference를 담겠다.
즉 caller가 전해준 변수를 직접 가리키겠다!
백문이 불여일견 예제!
i) SV 의 int[] (int type array)를 C함수에서 svOpenArrayHandle과 맵핑하기.
// SV
import "DPI-C" function void comput_unsized_int_array ( input int i_value[], output int result[];
// C
void compute_unsized_int_array(const svOpenArrayHandle i_value, svOpenArrayHandle result);
SV에서 output 방향으로 선언된 result[] argument는 pass by reference이다.
SV에서 모든 array타입은 C에서 svOpenArrayHandle 타입(DPI-defined)와 호환된다.
c함수에서 선언된 array arg는 pass by value, pass by reference 모두 svOpenArrayHandle타입으로 선언된다. 포인터처럼 (*) 붙일 필요 X
ii) SV의 struct 를 C함수에서 struct 타입과 맵핑하기.
// SV
`define BIT_ARRAY_SIZE 16
typedef struct {
byte aByte;
int anInt;
bit aBit;
longint aLongInt;
bit[`BIT_ARRAY_SIZE-1:0] aBitVector;
} dpi_c_ex_s;
import "DPI-C" function void compute_struct (
input dpi_c_ex_s i_value,
output dpi_c_ex_s result
);
// C
typedef struct dpi_c_ex_s {
char aChar;
int anInt;
svBit aBit;
long int aLongInt;
svBitVecVal aBitVector;
} dpi_c_ex_s;
void compute_struct(
const dpi_c_ex_s* i_value,
dpi_c_ex_s* output
);
아주 아주 아주 충격적인 사실은 struct type은 SV에서 output 뿐만 아니라 input으로 선언된 struct변수 (즉 call by value)에 대해서도 pointer 형태로 받는다는 사실이다.
** SystemVerilog LRM (시스템베릴로그 IEEE표준 공식 문서)에 따르면...
packed data(예를 들면 struct type, 여러 변수가 하나의 변수로 표현/참조되는 것을 말함)는 DPI 함수 호출 시,
argument로서 전달될 때, data의 reference가 전달된다.
즉, SV가 C함수를 호출하면서 struct타입을 그냥(input으로, 즉 call by value로) 넘겨줘도,
default가 struct의 reference가 넘겨진다는 뜻.
나의 추측은 struct type은 변수명이 struct 변수가 위치한 시작 주소를 뜻하지않을까 싶네. 마치 C에서 array 이름이 array시작 주소를 가리키는 것처럼.