Verilog配列とメモリ:多次元データ構造をマスターする
ネットまたは変数の 配列 宣言はスカラでもベクトルでも可能です。識別子名の後にアドレス範囲を指定することで任意の次元を作成でき、これを多次元配列と呼びます。配列は reg、wire、integer、real データ型で使用可能です。
reg y1 [11:0]; // スカラreg配列、深さ12、各1ビット幅
wire [0:7] y2 [3:0]; // 8ビットベクトルネット、深さ4
reg [7:0] y3 [0:1][0:3]; // 2次元配列、行2、列4、各8ビット幅
配列の特定要素にアクセスするには、各次元のインデックスを指定する必要があり、他の変数の式でも指定可能です。Verilogでサポートされる各データ型で配列を形成できます。
代入
y1 = 0; // 違法 - 全要素を一括代入不可
y2[0] = 8'ha2; // index=0に0xa2を代入
y2[2] = 8'h1c; // index=2に0x1cを代入
y3[1][2] = 8'hdd; // 行1、列2に0xddを代入
y3[0][0] = 8'haa; // 行0、列0に0xaaを代入
例
以下のコードは、様々な配列のモデリング、代入、アクセスを示しています。mem1 は8ビットベクトル、mem2 は深さ4の8ビットベクトル配列(範囲[0:3]で指定)、mem3 は4行2列の16ビットベクトル2次元配列です。これらに異なる値を代入して表示します。
module des ();
reg [7:0] mem1; // 8ビット幅regベクトル
reg [7:0] mem2 [0:3]; // 8ビット幅ベクトル配列、深さ=4
reg [15:0] mem3 [0:3][0:1]; // 16ビット幅ベクトル2次元配列、行=4、列=2
initial begin
int i;
mem1 = 8'ha9;
$display ("mem1 = 0x%0h", mem1);
mem2[0] = 8'haa;
mem2[1] = 8'hbb;
mem2[2] = 8'hcc;
mem2[3] = 8'hdd;
for(i = 0; i < 4; i = i+1) begin
$display("mem2[%0d] = 0x%0h", i, mem2[i]);
end
for(int i = 0; i < 4; i += 1) begin
for(int j = 0; j < 2; j += 1) begin
mem3[i][j] = i + j;
$display("mem3[%0d][%0d] = 0x%0h", i, j, mem3[i][j]);
end
end
end
endmodule
シミュレーションログ
ncsim> run mem1 = 0xa9 mem2[0] = 0xaa mem2[1] = 0xbb mem2[2] = 0xcc mem2[3] = 0xdd mem3[0][0] = 0x0 mem3[0][1] = 0x1 mem3[1][0] = 0x1 mem3[1][1] = 0x2 mem3[2][0] = 0x2 mem3[2][1] = 0x3 mem3[3][0] = 0x3 mem3[3][1] = 0x4 ncsim: *W,RNQUIE: Simulation is complete.
メモリ
メモリは、デジタル回路でデータと情報を格納するデジタル記憶要素です。RAMやROMが好例です。記憶要素は reg 型の1次元配列でモデリングでき、これを メモリ と呼びます。メモリの各要素は1ワードを表し、単一の配列インデックスで参照されます。
レジスタベクトル
Verilogベクトルは変数名の左側にサイズ範囲を指定して宣言し、変数のサイズに合ったフリップフロップとして実現されます。以下のコードでは、設計モジュールがクロック、リセット、読み書き制御信号を受け取ります。
16ビットの記憶要素 register を含み、書き込み時に更新し、読み取り時に現在値を返します。sel と wr が同じクロックエッジでハイのとき書き込み、sel がハイで wr がローのとき現在データを返します。
module des (
input clk, input rstn, input wr, input sel,
input [15:0] wdata, output [15:0] rdata
);
reg [15:0] register;
always @ (posedge clk) begin
if (!rstn) register <= 0;
else begin
if (sel & wr) register <= wdata;
else register <= register;
end
end
assign rdata = (sel & ~wr) ? register : 0;
endmodule
ハードウェア回路図では、書き込み制御ロジックがアクティブなときに16ビットフリップフロップが更新され、読み取り制御ロジックが設定されているときに現在値が返されることがわかります。
配列
この例では、register は4つのロケーションを持つ配列で、各幅は16ビットです。設計モジュールは特定のインデックスにアクセスするための追加入力信号 addr を受け取ります。
module des (
input clk, input rstn, input [1:0] addr,
input wr, input sel, input [15:0] wdata,
output [15:0] rdata
);
reg [15:0] register [0:3];
integer i;
always @ (posedge clk) begin
if (!rstn) begin
for (i = 0; i < 4; i = i+1) begin
register[i] <= 0;
end
end else begin
if (sel & wr) register[addr] <= wdata;
else register[addr] <= register[addr];
end
end
assign rdata = (sel & ~wr) ? register[addr] : 0;
endmodule
ハードウェア回路図では、配列の各インデックスが16ビットフリップフロップであり、入力アドレスで特定のフリップフロップセットにアクセスしていることがわかります。
Verilog