はじめに
以前「SystemVerilogのDPIでRustを使ってみた」という記事を書きましたが、この時は主にRustのプリミティブ型で扱える範囲内の記述をざっと試しただけでした。
その後、SystemVerilogで書いたRTLの検証モデルをRustで書くことが増えてきて、SystemVerilogの4値型を扱いたくなってきました。SystemVerilogの4値型はそのままRustのプリミティブ型としては扱えないので、そのために作成したライブラリと併せてご紹介します。
4値型とは
一般的にソフトウェアでいうビットとは0か1かの2通りの値を取ります。すなわち2値型です。
これを実現するデジタル回路においても同様に、ある配線の電圧が示す値を0か1であるとして設計するのですが、デジタル回路設計においては0/1以外の値も考える必要があり、それを扱うのが4値型です。
SystemVerilogにおける4値型は以下のように定義されています。
- 0: 0またはfalse
- 1: 1またはtrue
- x: 不定値
- z: ハイ・インピーダンス
0と1はそのままですが、x/zという2つが追加されています。xすなわち不定値は「0か1か分からない」という意味です。例えば、電源投入直後のメモリの値は、そのメモリを構成する半導体素子の個体差によってビット毎に0だったり1だったりします。このような場合、0か1かを事前に決めて設計することはできないので「xという特別な値が入っている」として設計・検証します。同様にzは「配線がどこにもつながっていないので0でも1でもない」といった状況を示すのに使われます。
この4値型をDPI(SystemVerilogとC言語間のFFIみたいなもの)経由でSystemVerilogからRustへ受け渡すことを考えます。
DPIから見た4値型
DPIによって4値型をどのように受け渡すかは、SystemVerilogの言語仕様(IEEE 1800-2017)で以下のように決まっています。
/*
* DPI representation of packed arrays.
* 2-state and 4-state vectors, exactly the same as PLI's avalue/bvalue.
*/
# ifndef VPI_VECVAL
# define VPI_VECVAL
typedef struct t_vpi_vecval {
uint32_t aval;
uint32_t bval;
} s_vpi_vecval, *p_vpi_vecval;
# endif
/* (a chunk of) packed logic array */
typedef s_vpi_vecval svLogicVecVal;
svLogicVecVal
が32ビットの4値を表す型で、DPIからはこのsvLogicVecVal
の配列がRustに渡されます。すなわち
# [repr(C)]
pub struct svLogicVecVal {
pub aval: u32,
pub bval: u32,
}
# [no_mangle]
pub extern "C" fn get_data(data: &[svLogicVecVal; 4]) {
}
というようなget_data
をCの関数としてエクスポートすれば、SystemVerilogから4値型を受け取ることができます。
ここで&[svLogicVecVal; 4]
と長さ4の配列にしているのでSystemVerilog側は 32 * 4 = 128 ビットを渡します。
import "DPI-C" function void get_data(
input logic [127:0] data
);
Sv4State
規格上定義されているsvLogicVecVal
のままでは扱いにくいので、Rustから扱いやすいように Sv4State
という型を用意しました。
例えば
# [no_mangle]
pub extern "C" fn get_data(data: &[svLogicVecVal; 4]) {
let sv_u32 = Sv4State::<u32>::from_dpi(data);
println!("{:x}", sv_u32[0]);
println!("{:x}", sv_u32[1]);
println!("{:x}", sv_u32[2]);
println!("{:x}", sv_u32[3]);
}
のように使います。
Sv4State
は std::fmt::LowerHex
を実装しているので {:x}
でいい感じに16進数表示できます。
また、Sv4State
のv
で32bitの値(x/zのビットは0としたもの)を、x
とz
でそれぞれx/zのビットフラグを得ることができます。
# [no_mangle]
pub extern "C" fn get_data(data: &[svLogicVecVal; 4]) {
let sv_u32 = Sv4State::<u32>::from_dpi(data);
println!("{:x}", sv_u32[0].v);
println!("{:x}", sv_u32[0].x);
println!("{:x}", sv_u32[0].z);
}
サンプル
以前の記事で紹介したサンプルに Sv4State
を使ったものも追加しました。