🧐

SystemVerilogにおけるQueueまとめ

に公開

SystemVerilog には Queue という特殊な配列型があります。Queue は可変長で、同じ型の要素を順序付きで格納するデータ構造です。

  • 任意の要素への定数時間アクセス
  • 先頭/末尾での定数時間挿入・削除

といった特徴を持ち、柔軟なデータ操作が可能です。


1. Queueの宣言と初期化

Queue の宣言は アンパック配列と同じ構文を用いますが、サイズに "$" を指定するのが特徴です。
最大サイズを制限したい場合は [$:N] を指定します(N は正の整数)。

例:

byte    q1[$];                 // バイト型のQueue
string  names[$] = {"Bob"};    // 初期化付き宣言
integer Q[$]     = {3, 2, 7};  // 整数Queue
bit     q2[$:255];             // 最大256要素に制限

宣言時に初期値を与えない場合は、空のQueue {} に初期化されます。

初期化方法

Queue は以下の2つの方法で初期化できます。

  • Unpacked array concatenation({})
  • Array assignment pattern('{ })
int q1[$];
q1 = {1, 2, 3};   // unpacked array concatenation
q1 = '{1, 2, 3};  // array assignment pattern

Unpacked array concatenation

{} を用いて要素や配列を並べて新しい配列を作る方法です。
Queue は一次元アンパック配列と同様に扱えるため、この構文で操作できます。

q1 = {1, 2, 3};
q1 = {q1, 4};   // {1,2,3,4}  ← Queueを右辺に含められる

Array assignment pattern

'{} を用いる方法です。構造体、共用体、アンパック配列(Queueを含む)の初期化に使われます。
ただし、要素はすべてターゲット配列の要素型と一致している必要があります。

q1 = '{1, 2, 3};
q1 = '{q1, 4};   // エラー(Queueをそのまま含められない)

array assignment pattern では replicationdefault値指定 が可能です。

q1 = '{9{1}};          // {1,1,1,1,1,1,1,1,1} replication記述で反復定義できる
q1 = '{2{4,5}};        // {4,5,4,5} 複数個のreplicationも可

int q2[$:9];
q2 = '{default:99};    // {99,99,99,99,99,99,99,99,99,99}
// キュー全体が初期化され、サイズは10となる (宣言されている最大サイズまで)

2. 基本操作

Queue は通常の配列操作に加え、柔軟なスライスや動的拡張をサポートします。

int q[$] = {10, 20, 30, 40};

int sub_q[$];
sub_q = q[1:2];  // {20,30}
sub_q = q[3:0];  // {} (範囲が逆の場合は空Queue)
sub_q = q[2:2];  // {30}

3. 組み込みメソッド

Queueは以下の組み込みメソッドを持ちます。

サイズ確認

int q[$] = {1, 2, 3};

// function int size();
$display("size = %0d", q.size()); // 3

挿入・削除

int q[$] = {1, 2, 3};

// function void insert(input integer index, input element_t item);
q.insert(1, 99); // {1, 99, 2, 3}

// function void delete( [input integer index] );
q.delete(2);     // {1, 99, 3}
q.delete();      // {}(全削除)

push / pop

int q[$] = {1, 2, 3};

// function void push_front(input element_t item);
q.push_front(0); // {0,1,2,3}

// function void push_back(input element_t item);
q.push_back(4);  // {0,1,2,3,4}

// function element_t pop_front();
int a = q.pop_front(); // a=0, q={1,2,3,4}

// function element_t pop_back();
int b = q.pop_back();  // b=4, q={1,2,3}

4. 要素参照の有効性

Queue 要素を参照した場合、操作によって参照が無効になる点に注意が必要です。

  • delete / pop_front / pop_back
    → 削除された要素の参照のみ無効化
  • Queue 全体を代入した場合
    すべての要素参照が無効化
int q[$] = {1,2,3};
int ref x = q[1];
q.pop_front();   // {2,3} → 元のq[1]参照は無効

5. 代入・連結による操作

Queue のメソッド操作は、代入や連結でも実現可能です。

int q[$] = {2,4,8};
int e = 1;

// push_back
q = {q, 6};              // {2,4,8,6}

// push_front
q = {e, q};              // {1,2,4,8,6}

// pop_front
q = q[1:$];              // {2,4,8,6}

// pop_back
q = q[0:$-1];            // {2,4,8}

// insert
int pos = 1;
q = {q[0:pos-1], 99, q[pos:$]}; // {2,99,4,8}

// delete
q = {};                  // {}

ただしこの方法では 既存の参照がすべて無効になる ため、参照の持続性を重視する場合は組み込みメソッドを使用してください。


まとめ

  • Queue は 可変長のアンパック配列であり、FIFO/LIFO 操作に便利。
  • push_front / push_back / pop_front / pop_back など専用メソッドを備える。
  • 代入や配列連結でも同様の操作は可能だが、要素参照が無効になる点に注意。

Discussion