|
Verilog HDL 簡易マニュアル
|
|
Verilog HDL 簡易マニュアル
|
|
========
|
|
========
|
|
|
|
|
|
## 代入
|
|
## はじめに
|
|
|
|
|
|
```
|
|
Verilogは、ハードウェア設計のための記述言語(HDL: Hardware Description Language)です。C言語などの手続き型プログラミング言語と異なり、**物理的な回路構造とその振る舞いを記述するための言語**です。手続き型言語では「命令を順に実行する」ことが基本となっていますが、HDLでは**同時に動作する回路構造(並列性)**を表現するのが特徴です。回路図をテキストとして記述していると考えると分かりやすいと思います。
|
|
wire a;
|
|
|
|
reg b;
|
|
Verilogでの基本構成は以下のようになります:
|
|
reg r;
|
|
|
|
|
|
- `module`:回路の構造ブロック(部品)
|
|
assign a = b;
|
|
- `input` / `output`:端子
|
|
always @(posedge clk) begin
|
|
- `wire` / `reg`:信号線やレジスタ(信号の種類)
|
|
r <= b;
|
|
- `wire` は常に接続されている線、と考えるとよいと思います、モジュール間の接続、ピンの入出力など、の用途に使います
|
|
end
|
|
- `reg` は記憶を持つ変数であり、フリップフロップ、状態変数など、クロックなどに応じて値が変化するような用途に使います
|
|
```
|
|
|
|
|
|
例:
|
|
`always`文の中で代入する場合は`<=`を使います。また、その変数は`reg`として宣言する必要があります。
|
|
```verilog
|
|
`assign`文は、「いつでも」左辺と右辺が同じであることを記述しているため、「特定の条件(クロックの立ち上がり、等)の時、左辺に代入する」ことを記述する`always`文の中では使えません。
|
|
wire a, b, y;
|
|
|
|
assign y = a & b; // wire への assign
|
|
|
|
|
|
## 数値
|
|
reg state;
|
|
|
|
always @(posedge clk) begin
|
|
```
|
|
state <= next_state; // reg への代入(順序回路)
|
|
8'b1111_0000 // 8bitの数値、二進法(b)で11110000
|
|
end
|
|
16'hfae3 // 16bitの数値、十六進法(h)でfae3
|
|
|
|
4'd12 // 4bitの数値、十進法(d)で12
|
|
reg sum;
|
|
2389 // 十進法で2389、ビット数は暗黙で32bitになる
|
|
always @(*) begin
|
|
```
|
|
sum = in1 + in2; // 組合せ回路の記述にも reg は使用可能
|
|
|
|
end
|
|
## 演算子
|
|
```
|
|
|
|
|
|
### 算術演算
|
|
- `assign`:**組合せ回路の論理式を記述するための代入文**。`wire` 型の信号に対して使用され、常に右辺の論理式に応じて出力が変化します。
|
|
|
|
- `always`:**条件に基づく処理(順序回路や条件に基づいた組み合わせ回路)を記述するブロック**。
|
|
```verilog
|
|
- `always @(posedge clk)` などクロックを指定して、**フリップフロップを使った順序回路の動作**を記述することができます。
|
|
a <= a + 1; // 二進法で加算
|
|
- `always @(*)` のようにセンシティビティリストに `*` を使って、**条件に基づいた組み合わせ回路**を記述することができます。
|
|
a <= a - 1; // 二進法で減算
|
|
|
|
a <= a * 3; // 二進法で乗算
|
|
ここでは Verilog の基本的な module の書き方や testbench の作成方法を簡潔に解説します。
|
|
a <= a / 3; // 二進法で除算
|
|
|
|
a <= a % 5; // 二進法で剰余演算
|
|
## 代入
|
|
```
|
|
|
|
|
|
```
|
|
C言語と同じ。
|
|
wire a;
|
|
|
|
reg b;
|
|
### 論理演算
|
|
reg r;
|
|
|
|
|
|
```verilog
|
|
assign a = b;
|
|
if (!a || b && c) // もし aが0 または (bが1 かつ cが1) ならば
|
|
always @(posedge clk) begin
|
|
```
|
|
r <= b;
|
|
|
|
end
|
|
C言語と同じ。
|
|
```
|
|
|
|
|
|
### ビットごと (bit-wise) の論理演算
|
|
`always`文の中で代入する場合は`<=`を使います。また、その変数は`reg`として宣言する必要があります。
|
|
|
|
`assign`文は、「いつでも」左辺と右辺が同じであることを記述しているため、「特定の条件(クロックの立ち上がり、等)の時、左辺に代入する」ことを記述する`always`文の中では使えません。
|
|
```verilog
|
|
|
|
a <= ~a; // 全ビットを反転
|
|
|
|
a <= a & b; // ビットごとに論理積
|
|
## 数値
|
|
a <= a | b; // ビットごとに論理和
|
|
|
|
a <= a ^ b; // ビットごとに排他的論理和
|
|
```
|
|
a <= a ~& b; // ビットごとにNAND
|
|
8'b1111_0000 // 8bitの数値、二進法(b)で11110000
|
|
a <= a ~| b; // ビットごとにNOR
|
|
16'hfae3 // 16bitの数値、十六進法(h)でfae3
|
|
```
|
|
4'd12 // 4bitの数値、十進法(d)で12
|
|
|
|
2389 // 十進法で2389、ビット数は暗黙で32bitになる
|
|
C言語と同じ。NANDやNORはC言語にはないが。
|
|
```
|
|
|
|
|
|
### シフト演算
|
|
## 演算子
|
|
|
|
|
|
```verilog
|
|
### 算術演算
|
|
a <= a >> 3; // 3bit論理右シフト
|
|
|
|
a <= a << 3; // 3bit論理左シフト
|
|
```verilog
|
|
a <= a >>> 3; // 3bit算術右シフト
|
|
a <= a + 1; // 二進法で加算
|
|
```
|
|
a <= a - 1; // 二進法で減算
|
|
|
|
a <= a * 3; // 二進法で乗算
|
|
C言語だと算術右シフトを選ぶ方法はないが、Verilog HDLでは`>>>`で書ける。`signed`な変数にのみ適用可能。
|
|
a <= a / 3; // 二進法で除算
|
|
|
|
a <= a % 5; // 二進法で剰余演算
|
|
### 比較演算子
|
|
```
|
|
|
|
|
|
```verilog
|
|
C言語と同じ。
|
|
if (a == 1'b1) ...; // もしaが1と等しかったら
|
|
|
|
if (a != 1'b1) ...; // もしaが1ではなかったら
|
|
### 論理演算
|
|
if (a < 2'b10) ...; // もしaを符号なし整数として解釈して2よりも小さかったら
|
|
|
|
if (a > 2'b01) ...; // もしaを符号なし整数として解釈して1よりも大きかったら
|
|
```verilog
|
|
```
|
|
if (!a || b && c) // もし aが0 または (bが1 かつ cが1) ならば
|
|
|
|
```
|
|
C言語と同じ。つまり、`a == b&c`は、`(a==b) & c`と解釈されるので注意が必要!
|
|
|
|
|
|
C言語と同じ。
|
|
### 条件演算子(選択)
|
|
|
|
|
|
### ビットごと (bit-wise) の論理演算
|
|
```verilog
|
|
|
|
a <= a ? b : c; // aが0でなければb、0ならばc
|
|
```verilog
|
|
```
|
|
a <= ~a; // 全ビットを反転
|
|
|
|
a <= a & b; // ビットごとに論理積
|
|
C言語と同じ。
|
|
a <= a | b; // ビットごとに論理和
|
|
|
|
a <= a ^ b; // ビットごとに排他的論理和
|
|
### ビットの取り出し
|
|
a <= a ~& b; // ビットごとにNAND
|
|
|
|
a <= a ~| b; // ビットごとにNOR
|
|
```verilog
|
|
```
|
|
wire [31:0] x;
|
|
|
|
wire msb;
|
|
C言語と同じ。NANDやNORはC言語にはないが。
|
|
wire [2:0] offset;
|
|
|
|
|
|
### シフト演算
|
|
assign msb = x[31];
|
|
|
|
assign offset = x[12:10];
|
|
```verilog
|
|
```
|
|
a <= a >> 3; // 3bit論理右シフト
|
|
|
|
a <= a << 3; // 3bit論理左シフト
|
|
C言語だと配列の宣言は`int array[32]`のようになるが、Verilog HDL では`wire [31:0] x;`のように逆順になる。取り出すときは同じ。
|
|
a <= a >>> 3; // 3bit算術右シフト
|
|
|
|
```
|
|
### 連結と複製
|
|
|
|
|
|
C言語だと算術右シフトを選ぶ方法はないが、Verilog HDLでは`>>>`で書ける。`signed`な変数にのみ適用可能。
|
|
```verilog
|
|
|
|
{{16{x[15]}}, x} // 16bit の値 x を 32bit に符号拡張
|
|
### 比較演算子
|
|
{{11{ins[31]}}, ins[31], ins[19:12], ins[20], ins[30:21], {1'b0}} // RISC-V の JAL 命令の即値のデコード
|
|
|
|
```
|
|
```verilog
|
|
|
|
if (a == 1'b1) ...; // もしaが1と等しかったら
|
|
## 構文
|
|
if (a != 1'b1) ...; // もしaが1ではなかったら
|
|
|
|
if (a < 2'b10) ...; // もしaを符号なし整数として解釈して2よりも小さかったら
|
|
### if
|
|
if (a > 2'b01) ...; // もしaを符号なし整数として解釈して1よりも大きかったら
|
|
C言語では`{`から`}`がブロックになりましたが、Verilog HDL では Pascal 風に`begin`から`end`でブロックを作ります。一文だけならばぶら下げることも可能です。
|
|
```
|
|
|
|
|
|
```verilog
|
|
C言語と同じ。つまり、`a == b&c`は、`(a==b) & c`と解釈されるので注意が必要!
|
|
if (条件式1)
|
|
|
|
式1;
|
|
### 条件演算子(選択)
|
|
else if (条件式2)
|
|
|
|
begin
|
|
```verilog
|
|
式2;
|
|
a <= a ? b : c; // aが0でなければb、0ならばc
|
|
式3;
|
|
```
|
|
end
|
|
|
|
else
|
|
C言語と同じ。
|
|
式4;
|
|
|
|
```
|
|
### ビットの取り出し
|
|
|
|
|
|
### for
|
|
```verilog
|
|
|
|
wire [31:0] x;
|
|
```verilog
|
|
wire msb;
|
|
for (i = 0; i < N; i = i + 1) 式;
|
|
wire [2:0] offset;
|
|
```
|
|
|
|
|
|
assign msb = x[31];
|
|
C言語と同じ。
|
|
assign offset = x[12:10];
|
|
|
|
```
|
|
### case
|
|
|
|
|
|
C言語だと配列の宣言は`int array[32]`のようになるが、Verilog HDL では`wire [31:0] x;`のように逆順になる。取り出すときは同じ。
|
|
C言語の`switch`文に相当します。
|
|
|
|
|
|
### 連結と複製
|
|
```verilog
|
|
|
|
case (信号名)
|
|
```verilog
|
|
値1: 式1;
|
|
{{16{x[15]}}, x} // 16bit の値 x を 32bit に符号拡張
|
|
値2: 式2;
|
|
{{11{ins[31]}}, ins[31], ins[19:12], ins[20], ins[30:21], {1'b0}} // RISC-V の JAL 命令の即値のデコード
|
|
値3: begin
|
|
```
|
|
式3;
|
|
|
|
式4;
|
|
## 構文
|
|
end
|
|
|
|
default: 式5;
|
|
### if
|
|
endcase
|
|
C言語では`{`から`}`がブロックになりましたが、Verilog HDL では Pascal 風に`begin`から`end`でブロックを作ります。一文だけならばぶら下げることも可能です。
|
|
```
|
|
|
|
### casex
|
|
```verilog
|
|
|
|
if (条件式1)
|
|
`x`を don't care として使用可能です。
|
|
式1;
|
|
|
|
else if (条件式2)
|
|
```verilog
|
|
begin
|
|
casex (ins[31:24])
|
|
式2;
|
|
8'b1000_00xx: d <= 3'b001; // 8'b1000_0000, 8'b1000_0001, 8'b1000_0010, 8'b1000_0011 の四種にマッチ
|
|
式3;
|
|
8'b0100_xxxx: d <= 3'b010; // 十六通りの値にマッチ
|
|
end
|
|
default : d <= 3'b000;
|
|
else
|
|
endcase
|
|
式4;
|
|
```
|
|
```
|
|
|
|
|
|
### function
|
|
### for
|
|
|
|
|
|
構文は以下のようになる。
|
|
```verilog
|
|
|
|
for (i = 0; i < N; i = i + 1) 式;
|
|
```verilog
|
|
```
|
|
function 返り値の型 関数名;
|
|
|
|
引数1;
|
|
C言語と同じ。
|
|
引数2;
|
|
|
|
|
|
### case
|
|
関数名 = 式;
|
|
|
|
endfunction
|
|
C言語の`switch`文に相当します。
|
|
```
|
|
|
|
|
|
```verilog
|
|
例えば、
|
|
case (信号名)
|
|
|
|
値1: 式1;
|
|
```verilog
|
|
値2: 式2;
|
|
function [7:0] dec38;
|
|
値3: begin
|
|
input [2:0] x;
|
|
式3;
|
|
|
|
式4;
|
|
dec38 = 0;
|
|
end
|
|
dec38[x] = 1;
|
|
default: 式5;
|
|
endfunction
|
|
endcase
|
|
```
|
|
```
|
|
|
|
### casex
|
|
引数がない関数は作れません。また、`input wire`とするとエラーです。
|
|
|
|
|
|
`x`を don't care として使用可能です。
|
|
## 組み合わせ回路を作るときの注意
|
|
|
|
|
|
```verilog
|
|
組み合わせ回路を作るため[^1]に`if`文や`case`文を使う場合、**必ず全パターンを記述してください**。つまり、`else`や`default`を必ず書いて、値を代入してくださいということです。
|
|
casex (ins[31:24])
|
|
|
|
8'b1000_00xx: d <= 3'b001; // 8'b1000_0000, 8'b1000_0001, 8'b1000_0010, 8'b1000_0011 の四種にマッチ
|
|
|
|
8'b0100_xxxx: d <= 3'b010; // 十六通りの値にマッチ
|
|
重要なのは、**全ての実行経路で値を代入する必要がある**ということです(が、そのうえでVivadoが「確かにすべての実行経路で値が代入される」と分かる書き方でないといけません)。
|
|
default : d <= 3'b000;
|
|
|
|
endcase
|
|
これは、特定の条件の時に代入されない → その場合信号が変化しない → 信号を記憶しなければ と解釈されるためで、ラッチが生成されてしまいます。
|
|
```
|
|
|
|
|
|
ラッチが意図せずできた場合、ほぼ確実に、ハードウェアでは動かない回路が完成します。
|
|
### function
|
|
ラッチができないよう、心掛けてください。
|
|
|
|
|
|
構文は以下のようになる。
|
|
例えば、以下のように書くとラッチが生成されます。
|
|
|
|
|
|
```verilog
|
|
```verilog
|
|
function 返り値の型 関数名;
|
|
always @(*) begin
|
|
引数1;
|
|
case (ins[31:24])
|
|
引数2;
|
|
8'b1000_0000: d <= 3'b001;
|
|
|
|
8'b1000_0001: d <= 3'b010;
|
|
関数名 = 式;
|
|
// ins[31:24]の値が8'b1000_0000でも8'b1000_0001でもない場合、
|
|
endfunction
|
|
// dに値が代入されない
|
|
```
|
|
// →dの値は変化しない
|
|
|
|
// →dの値を記憶しておく必要がある
|
|
例えば、
|
|
// →ラッチが生成される
|
|
|
|
endcase
|
|
```verilog
|
|
end
|
|
function [7:0] dec38;
|
|
```
|
|
input [2:0] x;
|
|
|
|
|
|
組み合わせ回路を作る(ラッチが生成されないようにする)ためには、以下のように記述してください。
|
|
dec38 = 0;
|
|
|
|
dec38[x] = 1;
|
|
```verilog
|
|
endfunction
|
|
always @(*) begin
|
|
```
|
|
case (ins[31:24])
|
|
|
|
8'b1000_0000: d <= 3'b001;
|
|
引数がない関数は作れません。また、`input wire`とするとエラーです。
|
|
8'b1000_0001: d <= 3'b010;
|
|
|
|
default: d <= 3'b000;
|
|
## 組み合わせ回路を作るときの注意
|
|
// ins[31:24]の値が8'b1000_0000または8'b1000_0001のどちらかにしかならないのだとしても、
|
|
|
|
// あるいはins[31:24]の値が8'b1000_0000でも8'b1000_0001でもない場合にはdの値を使わないのだとしても、
|
|
組み合わせ回路を作るため[^1]に`if`文や`case`文を使う場合、**必ず全パターンを記述してください**。つまり、`else`や`default`を必ず書いて、値を代入してくださいということです。
|
|
// default:を記述してdに何らかの値を代入しておく
|
|
|
|
endcase
|
|
|
|
end
|
|
重要なのは、**全ての実行経路で値を代入する必要がある**ということです(が、そのうえでVivadoが「確かにすべての実行経路で値が代入される」と分かる書き方でないといけません)。
|
|
```
|
|
|
|
|
|
これは、特定の条件の時に代入されない → その場合信号が変化しない → 信号を記憶しなければ と解釈されるためで、ラッチが生成されてしまいます。
|
|
|
|
|
|
命令セットにないから来ないはず、という発想で書かないのは**間違いです**。C言語等では、本当に来ないなら記述しなくても問題なく動作しますが、HDL の場合はそうではありません。論理合成ツールからは、回路の入力として何が来るか把握できないため、何が来ても大丈夫なように回路を構成しようとします。そのため、不要なラッチができてしまいます。
|
|
ラッチが意図せずできた場合、ほぼ確実に、ハードウェアでは動かない回路が完成します。
|
|
|
|
ラッチができないよう、心掛けてください。
|
|
## コンパイラ指示子
|
|
|
|
|
|
例えば、以下のように書くとラッチが生成されます。
|
|
### マクロ
|
|
|
|
|
|
```verilog
|
|
C言語の`#define`のようなことができます。
|
|
always @(*) begin
|
|
|
|
case (ins[31:24])
|
|
#### 定義の仕方
|
|
8'b1000_0000: d <= 3'b001;
|
|
|
|
8'b1000_0001: d <= 3'b010;
|
|
```verilog
|
|
// ins[31:24]の値が8'b1000_0000でも8'b1000_0001でもない場合、
|
|
`define シンボル 値
|
|
// dに値が代入されない
|
|
```
|
|
// →dの値は変化しない
|
|
|
|
// →dの値を記憶しておく必要がある
|
|
#### 参照の仕方
|
|
// →ラッチが生成される
|
|
|
|
endcase
|
|
```verilog
|
|
end
|
|
`シンボル
|
|
```
|
|
```
|
|
|
|
|
|
組み合わせ回路を作る(ラッチが生成されないようにする)ためには、以下のように記述してください。
|
|
#### 例
|
|
|
|
|
|
```verilog
|
|
```verilog
|
|
always @(*) begin
|
|
`define sign 1
|
|
case (ins[31:24])
|
|
|
|
8'b1000_0000: d <= 3'b001;
|
|
// ...
|
|
8'b1000_0001: d <= 3'b010;
|
|
|
|
default: d <= 3'b000;
|
|
line = `sign; // line = 1 と同じ。
|
|
// ins[31:24]の値が8'b1000_0000または8'b1000_0001のどちらかにしかならないのだとしても、
|
|
```
|
|
// あるいはins[31:24]の値が8'b1000_0000でも8'b1000_0001でもない場合にはdの値を使わないのだとしても、
|
|
|
|
// default:を記述してdに何らかの値を代入しておく
|
|
`` `define ``による定義は、モジュール定義内でも行えます。
|
|
endcase
|
|
|
|
end
|
|
### インクルード
|
|
```
|
|
|
|
|
|
C言語の`#include`のようなことができます。
|
|
|
|
|
|
命令セットにないから来ないはず、という発想で書かないのは**間違いです**。C言語等では、本当に来ないなら記述しなくても問題なく動作しますが、HDL の場合はそうではありません。論理合成ツールからは、回路の入力として何が来るか把握できないため、何が来ても大丈夫なように回路を構成しようとします。そのため、不要なラッチができてしまいます。
|
|
```verilog
|
|
|
|
`include "ファイル名"
|
|
## コンパイラ指示子
|
|
```
|
|
|
|
|
|
### マクロ
|
|
複数の`.v`ファイルに分割して設計するときには、C言語のヘッダファイルのように、マクロ定義だけ集めたファイルを作り、インクルードするとよいでしょう。
|
|
|
|
|
|
C言語の`#define`のようなことができます。
|
|
|
|
|
|
## 構文エラーの原因
|
|
#### 定義の仕方
|
|
|
|
|
|
Verilogの処理系のエラーメッセージはお世辞にもわかりやすいとは言えません.
|
|
```verilog
|
|
例えば,以下のような32ビットの加算を行うモジュールを考えてみます.
|
|
`define シンボル 値
|
|
|
|
```
|
|
```verilog
|
|
|
|
module adder(
|
|
#### 参照の仕方
|
|
input wire [31:0] a,
|
|
|
|
input wire [31:0] b,
|
|
```verilog
|
|
output wire [31:0] c
|
|
`シンボル
|
|
);
|
|
```
|
|
assign c = a + b
|
|
|
|
endmodule
|
|
#### 例
|
|
```
|
|
|
|
|
|
```verilog
|
|
これをフリーの処理系であるIcarus Verilogでコンパイルしようとすると,
|
|
`define sign 1
|
|
|
|
|
|
```
|
|
// ...
|
|
$ iverilog a.v
|
|
|
|
a.v:7: syntax error
|
|
line = `sign; // line = 1 と同じ。
|
|
I give up.
|
|
```
|
|
```
|
|
|
|
|
|
`` `define ``による定義は、モジュール定義内でも行えます。
|
|
と表示されます(Vivadoでも恐らく似たようなメッセージが表示されるでしょう).
|
|
|
|
エラーメッセージでは`a.v`の7行目がおかしいとされていますが,この7行目は`endmodule`としか書いておらず間違っているようには見えません.
|
|
### インクルード
|
|
エラーの原因は今回の例では6行目の`assign c = a + b`の最後に`;`がないことです.
|
|
|
|
このように文法的なエラーの場合はエラーメッセージで示された行より前に原因があることも多いです.
|
|
C言語の`#include`のようなことができます。
|
|
その行だけを眺めてもエラーの原因がわからない場合はそれより前を眺めてみましょう.
|
|
|
|
|
|
```verilog
|
|
ちなみに,なぜこういうことが起こるかを簡単に説明すると,一般に言語処理系には構文解析という文字列を構文木と呼ばれる木構造に変換するフェーズがあり,ここまで読んだ結果がこれだから次はあれかこれかが来るだろうという風に予測しながら木を構成していくからです.
|
|
`include "ファイル名"
|
|
つまり,上の例では`a+b`まで読んだ後は`;`が来るかあるいは`- c`のようにまだ式が続くかという予想を立て,予想外の`endmodule`が来た時点でエラーとなるわけです.
|
|
```
|
|
|
|
|
|
----
|
|
複数の`.v`ファイルに分割して設計するときには、C言語のヘッダファイルのように、マクロ定義だけ集めたファイルを作り、インクルードするとよいでしょう。
|
|
[^1]: 組み合わせ回路でない、つまりフリップフロップやRAMを作る際の`if`文(ライトイネーブル信号やリセットなど)の場合、全パターンを記述しなくてもかまいません。このような素子はもともと、代入されない時は元の値を保持する機能があるためです。組み合わせ回路にはそのような機能がないため、「元の値を保持したい」風の記述をしてはいけません(そのような記述をすると組み合わせ回路ではなくなり、特にラッチが生成されてしまいます)。 |
|
|
|
|
|
|
|
|
|
## 構文エラーの原因
|
|
|
|
|
|
|
|
Verilogの処理系のエラーメッセージはお世辞にもわかりやすいとは言えません.
|
|
|
|
例えば,以下のような32ビットの加算を行うモジュールを考えてみます.
|
|
|
|
|
|
|
|
```verilog
|
|
|
|
module adder(
|
|
|
|
input wire [31:0] a,
|
|
|
|
input wire [31:0] b,
|
|
|
|
output wire [31:0] c
|
|
|
|
);
|
|
|
|
assign c = a + b
|
|
|
|
endmodule
|
|
|
|
```
|
|
|
|
|
|
|
|
これをフリーの処理系であるIcarus Verilogでコンパイルしようとすると,
|
|
|
|
|
|
|
|
```
|
|
|
|
$ iverilog a.v
|
|
|
|
a.v:7: syntax error
|
|
|
|
I give up.
|
|
|
|
```
|
|
|
|
|
|
|
|
と表示されます(Vivadoでも恐らく似たようなメッセージが表示されるでしょう).
|
|
|
|
エラーメッセージでは`a.v`の7行目がおかしいとされていますが,この7行目は`endmodule`としか書いておらず間違っているようには見えません.
|
|
|
|
エラーの原因は今回の例では6行目の`assign c = a + b`の最後に`;`がないことです.
|
|
|
|
このように文法的なエラーの場合はエラーメッセージで示された行より前に原因があることも多いです.
|
|
|
|
その行だけを眺めてもエラーの原因がわからない場合はそれより前を眺めてみましょう.
|
|
|
|
|
|
|
|
ちなみに,なぜこういうことが起こるかを簡単に説明すると,一般に言語処理系には構文解析という文字列を構文木と呼ばれる木構造に変換するフェーズがあり,ここまで読んだ結果がこれだから次はあれかこれかが来るだろうという風に予測しながら木を構成していくからです.
|
|
|
|
つまり,上の例では`a+b`まで読んだ後は`;`が来るかあるいは`- c`のようにまだ式が続くかという予想を立て,予想外の`endmodule`が来た時点でエラーとなるわけです.
|
|
|
|
|
|
|
|
----
|
|
|
|
[^1]: 組み合わせ回路でない、つまりフリップフロップやRAMを作る際の`if`文(ライトイネーブル信号やリセットなど)の場合、全パターンを記述しなくてもかまいません。このような素子はもともと、代入されない時は元の値を保持する機能があるためです。組み合わせ回路にはそのような機能がないため、「元の値を保持したい」風の記述をしてはいけません(そのような記述をすると組み合わせ回路ではなくなり、特にラッチが生成されてしまいます)。 |