LoginSignup
127
106

More than 5 years have passed since last update.

どんな記事?

自分がみた感じVerilogの文法説明書みたいなページはたくさんあるのですが、軽い感じで読めるサイトが少ないなと思って書いてみました。
本ページでは、Verilogの文法を必要最低限理解するために必要なことを説明していきます。筆者もVerilogを始めたてなので、細かい言葉遣いに間違いがあるかもしれません...
最終的にプロセッサー(もどき)をサンプルとして扱いたいと思っています。
一回で書くのは大変なので、何回かに分けて投稿したいと思います。
今回は、変数の代入なりモジュール宣言なり、if分岐なりを説明します。

wire変数とreg変数

Verilogの変数には、wire(ワイヤ)とreg(レジスタ)の2つの型みたいなものがあります。C言語などからきた人にはここがとてもわかりにくいと思います(体験談)。

wire変数

wire変数は、モジュール(部品)同士をつなぐ配線をあらわします。C言語の変数と違い、値を保持する入れ物ではなく 値が流れている線 のイメージです。
たとえば、32bitのwire変数は以下のように宣言します。

wire [31:0] hoge;

ハードウェア記述なので何bitなのかを宣言しなければなりません。wire変数は、常に値の更新が起こりうる変数です。

reg変数

reg変数は、C言語などの変数と同様、値を保持しておく入れ物です。
たとえば、32bitのreg変数は以下のように宣言します。

reg [31:0] hoge;

reg変数はwire変数とは違って、値が流れているわけではないので値を代入するタイミングを指定する必要があります。

assign文とalways文

ものすごく大雑把なことを言うと、ワイヤ変数への代入はassign文、レジスタ変数への代入はalways文中でおこなうと考えてください(実際には、レジスタ変数はalways文以外での書き換えられます)。

assign文

ワイヤ同士をつなげたい場合assign文を使用します。
たとえば、32bitの符号付きワイヤaとbの和をワイヤcに流したい場合、以下のように書きます。

wire signed [31:0] a, b, c;
assign c = a + b;

Verilogでは、なにも宣言しないと符号なしの変数になるので注意してください。
また以下のように、wire変数の初期化の時点でワイヤをつなげることもできます。

wire [31:0] a, b;
wire [31:0] c = a + b;

always文

reg変数はwire変数とは違って、値が流れているわけではないので値を代入するタイミングを指定する必要があります。このタイミングの指定をハードウェアではクロックで行います。そして、タイミングの指定方法がalways文です。always文は、あるタイミングで起こしたい処理を書くことができます。

たとえば、クロックの立ち上がり時に行いたい処理は以下のブロックのなかに書きます(以下、クロックはCLKというreg変数で表します)。

reg [31:0] a;
always @(posedge CLK) begin
    a <= 0; //CLKの立ち上がり時に、aに0を代入
end

ここで、ブロックの指定にはC言語の{, }の代わりにbegin, endを使用します。また、posedge以外にnegedgeも使用できます。
reg変数への代入は、initial文、task、function中でも可能ですが、これはあとで触れます。

ブロッキング代入とノンブロッキング代入

Verilogの代入には2種類の代入法があります。違いとしては、同時に代入を行うのか、順番に行うのかということです。
なんで2種類必要かというと、ハードウェアなのでC言語みたいに コードを上から読んでいくわけではない です。なので、あるタイミングで、この代入とこの代入を同時に行いたい、という場合があります。

ブロッキング代入

ブロッキング代入は、順番に行う代入です。=を使用します。
たとえば、レジスタaとbの和をcに代入するときは以下のようになります。

reg [31:0] a, b, c;
c = a+ b;

ノンブロッキング代入

ノンブロッキング代入は、同時に行う代入です。
たとえば、レジスタaとbの値を入れ替えたいときは以下のようになります。

reg [31:0] a, b;
a <= b;
b <= a;

C言語では必要なスワップ用の一時変数が不要になっています。
はじめのうちは、reg変数への代入はノンブロッキング代入しておけばなんとかなります(たぶん)。

モジュールの作成

モジュールとは、回路を構成している部品だと思っていただければ大丈夫です。
ここでは指定されたアドレスの値を返すメモリを考えてみましょう。コードはこんな感じです。

modeule MEM(addr, data) ;
    input [9:0] addr;
    output [31:0] data;

    reg [31:0] mem[1023:0]; //4 Kbyte memory
    assign data = mem[addr];
endmodule

最初にinputoutputで入力と出力を指定します。基本的に入出力はワイヤで行います。なお、mem[1023:0]は配列を表します。
メモリはレジスタとして値を保持していて、それをワイヤに流すという構図になっています。出力のdataはワイヤなのでassignを使用していることに注意してください。

function文

function文はC言語の関数に相当します。ただし、関数名と返り値の名前が一致していなくてはなりません。
以下に、入力の値に1足した数を返すfunctionのコードを示します。

wire [31:0] a,result;
function [31:0] calctest;
    input [31:0] inp;
    calctest = inp + 1;
endfunction
//呼び出すとき
assign result = calctest(a);

if文とswitch文と三項演算子

if文とswitch文

if文やswitch文はfunctionおよびalways文中でしか使用できません。以下にサンプルコードを上げておきます。

function calcif;
    input [31:0] inp;
    if(inp == 1) begin
        calcif = 0;
    end else if (inp == 2) begin
        calcif = 1;
    end else begin
        calcif = 2;
    end
endfunction
function calccase;
    input [31:0] inp;
    case(inp)
        32'd1: calccase = 0;
        32'd2: calccase = 1;
        defaule: calccase = 2;
    endcase
endfunction

ここで、32'dは32bitの10進数表記を表します。

三項演算子

三項演算子はどこでも使用できます。(条件式) ? (真のときの値) : (偽のときの値)と書きます。boolが真のときのみインクリメントする処理は以下のようになります。

wire bool; //指定しないと1bit
wire a = (bool) ? a+1 : a;
127
106
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
127
106