SystemVerilogに登場するchandleについて知らなかったので調査してみることにした。
要約するとchandleはC/C++のポインタをSystemVerilogから扱うためのデータ型である。
DPI(Direct Programming Interface)を使用してC/C++の関数を呼び出す際に、特に使われるものだ。
chandleの基本概念
chandleとは
chandleは、C/C++のポインタ値を格納するための不透明なハンドルと定義することができる。
SystemVerilogからは、このハンドルの値自体を直接操作することはできないが、DPI関数を通じてC/C++の関数に渡すことができる。
chandleの宣言と使用方法
基本的な宣言
SystemVerilogからは以下のようにして宣言する。最初はnullを割り当てておく。
// chandle変数の宣言
chandle my_handle;
// 初期化(nullポインタ)
initial begin
my_handle = null;
end
DPI関数との組み合わせ
C++の関数がポインタを渡すときに、chandleで受け取るようにする。
chandleの中身は直接SystemVerilogからはアクセスできないので、chandleの値を取得するのに、get_value()をさらに定義している。
// C/C++関数の宣言
import "DPI-C" function chandle create_object(input string name);
import "DPI-C" function void destroy_object(input chandle obj);
import "DPI-C" function int get_value(input chandle obj);
module testbench;
chandle obj_handle;
initial begin
// オブジェクトの作成
obj_handle = create_object("test_object");
// オブジェクトの使用
if (obj_handle != null) begin
int value = get_value(obj_handle);
$display("Value: %d", value);
end
// オブジェクトの破棄
destroy_object(obj_handle);
obj_handle = null;
end
endmodule
例えばの例だが、create_object()、destroy_object()、get_value()は以下のようにしてDPI-Cの関数として定義される。
#include <stdlib.h> #include <string.h> #include <stdio.h> // 任意の構造体 typedef struct { char name[256]; int value; } MyObject; // オブジェクトの生成 void* create_object(const char* name) { MyObject* obj = (MyObject*)malloc(sizeof(MyObject)); if (obj) { strncpy(obj->name, name, sizeof(obj->name)); obj->name[sizeof(obj->name) - 1] = '\0'; obj->value = 42; // 仮の値 } return (void*)obj; } // オブジェクトの破棄 void destroy_object(void* obj) { free(obj); } // 値の取得 int get_value(void* obj) { if (obj == NULL) return -1; MyObject* myobj = (MyObject*)obj; return myobj->value; }
実用的な使用例
1. リファレンスモデルの管理
RISC-Vプロセッサの検証において、RVVI-APIを使用した例:
module rvvi_testbench;
import "DPI-C" function chandle rvviRefModelCreate(
input string config,
input string isa,
input string options
);
import "DPI-C" function void rvviRefModelDestroy(
input chandle ref
);
import "DPI-C" function longint unsigned rvviRefModelGetGPR(
input chandle ref,
input int hart,
input int reg
);
chandle ref_model;
initial begin
// リファレンスモデルの作成
ref_model = rvviRefModelCreate("config.json", "rv64gc", "");
if (ref_model == null) begin
$error("Failed to create reference model");
$finish;
end
// テスト実行...
// リファレンスモデルの破棄
rvviRefModelDestroy(ref_model);
ref_model = null;
end
endmodule
2. メモリ管理の例
module memory_test;
import "DPI-C" function chandle malloc(input int size);
import "DPI-C" function void free(input chandle ptr);
import "DPI-C" function void write_memory(
input chandle ptr,
input int offset,
input int data
);
import "DPI-C" function int read_memory(
input chandle ptr,
input int offset
);
chandle memory_ptr;
initial begin
// メモリの確保
memory_ptr = malloc(1024);
if (memory_ptr != null) begin
// メモリへの書き込み
write_memory(memory_ptr, 0, 0x12345678);
// メモリからの読み出し
int data = read_memory(memory_ptr, 0);
$display("Read data: 0x%x", data);
// メモリの解放
free(memory_ptr);
memory_ptr = null;
end
end
endmodule
3. オブジェクト指向的な使用例
module object_test;
import "DPI-C" function chandle create_counter();
import "DPI-C" function void increment(input chandle counter);
import "DPI-C" function int get_count(input chandle counter);
import "DPI-C" function void reset_counter(input chandle counter);
import "DPI-C" function void delete_counter(input chandle counter);
chandle counter1, counter2;
initial begin
// カウンターオブジェクトの作成
counter1 = create_counter();
counter2 = create_counter();
// カウンターの操作
increment(counter1);
increment(counter1);
increment(counter2);
$display("Counter1: %d", get_count(counter1));
$display("Counter2: %d", get_count(counter2));
// カウンターのリセット
reset_counter(counter1);
$display("After reset, Counter1: %d", get_count(counter1));
// オブジェクトの破棄
delete_counter(counter1);
delete_counter(counter2);
counter1 = null;
counter2 = null;
end
endmodule