よもぎのメモ帳

備忘録的な感じで技術的なことをストックしていきます。

私がVerilog HDLで躓いたところ

久しぶりの投稿₍₍ (ง ˘ω˘ )ว ⁾⁾

f:id:y0m0g1:20181121152357p:plain
社畜ちゃん台詞メーカーより

長らく書いていなかったので、反省しています。 書きたいことはいくつかあるんですが! 忙しくてなかなか筆が進みません (言い訳)

久しぶりのエントリですが、今回は Verilog HDL について扱っていきます。 学部実験「マイクロプロセッサの設計と実装」参考・旧サイト にて、 Verilogと戯れて つらかった 楽しかった思い出を振り返ろうと思います。

Verilogを書くときに、私が躓き、調べたことをまとめてみます。参考に慣れば幸いです。

※私のイメージが入っています。もしかしたら正しくないかも

躓いたところを一つずつ

思い出しつつ書いているので順番は適当です、許してください。

wireにはassignを、regにはinitial/always文を

// wire型
wire poyo;
assign poyo = punipuni;

// reg型
reg fuwa
always @(posedge clk) begin
    fuwa <= howa;
end

wire 型変数はワイヤ(導線)で、 assign 文で導線をつなげる、割り当てるってイメージでしょうか。

一方で reg 型変数はレジスタで、 always 文などで タイミングを指定して代入 していきます。そんなイメージです。

数字のあれこれ

5'b00011 // 2進5bit

32'hdeadbeaf // 16進32bit

8'd12345// 10進8bit

数字は次のような書式になっています

◯ ' ⬛XXXXX

ここで、

  • ◯:ビット幅を表す
  • ⬛:あとに続く数字の書式を表す b=2進数、h=16進数、d=10進数

たとえば、7bit幅で21を表したいときには、 7'b0010101 7'd21 7'h15 のようにかけると思います。

符合拡張、ゼロ拡張の仕方

input wire fuwa[7:0];
output wire poyo[31:0];

// 符合拡張
assign poyo = $signed({fuwa[7:0]});
// assign poyo = $signed(fuwa); でもきっと同じ
// assign poyo = {24{fuwa[7]} , fuwa};

// ゼロ拡張
assign poyo = $signed({1'b0, fuwa[7:0]});
// assign poyo = $unsigned(fuwa);
// assign poyo = {24'b0, fuwa};

$signed()関数を使うと自動的に符合拡張してくれます。 最上位ビットを繰り返して {} で結合してもいいんですが、繰り返し回数ミスしそうで怖いので私は使いませんでした。

ゼロ拡張は、{1'b0, fuwa} みたいに上に0をつけて $signed()関数を使うやり方を見ておおおーって使っていました。 よく考えたら $unsigned()関数があるしそちらを使ったほうがいいかもしれない。

おそらく配線上ではどれも変わらないのでは?と思います。

moduleとポートと

wire poyoyo[31:0];
wire puwawa[7:0];

// モジュールの呼び出し
fuwafuwa fuwa1(
   .poyo(poyoyo),
   .puwa(puwawa)
);

// モジュールの記述
module fuwafuwa(
    input wire poyo[31:0],
    input wire puwa[7:0]
);
    // fuwafuwaの処理
endmodule

モジュールはVerilogで一つの大きな単位となっていて、他の言語で言うClassみたいなイメージで扱いました。

モジュールの呼び出しには

  • 呼び出すモジュール
  • モジュールのインスタンス名のようなもの
  • 接続されるポートと、接続するwire/reg …… .port(wire) の書式

.port() の部分は省略でき、その場合にはモジュールの宣言と同じ順序で引数が代入されるのですが、 間違える場合もあるので、少し面倒でも .poyo(poyoyo) のようにポート名も書きました。

モジュールの記述はいくつかの書式があるのですが、 VSCodeでの折りたたむ機能で、入出力ポートだけは見たい! みたいな場合を想定して、 上のような記述にしていました。

if/case文はmodule内に書けない

module mod(
    input wire flag,
    input wire[31:0] poyoyo,
    output wire[31:0] poyo
);

// だめな例
if (flag == 1'b1) begin
    assign poyo = poyoyo;
end else begin
    assign poyo = 32'b0;
end

// いい例
assign poyo = fun(flag, poyoyo);
function fun[31:0];
    input flag;
    input [31:0] po[31:0];

    if (flag == 1'b1) begin
        fun = po;
    end else begin
        fun = 32'b0;
    end
endfunction

endmodule

function 文や always 文内で if / case 文がかけます。

function は戻り値をひとつだけ取ることができて、function名にイコールでつなぐことで返すことができます。

また、 if 文では else を、 case 文では default を必ず入れましょう。 入れないと例外的な入力が来たときにフリップフロップが生まれてしまうので、だめっぽいです


マクロ定数のような何か

// define文でマクロ定数宣言
`define ENABLE 1

// マクロ定数を用いる
assign poyo = `ENABLE

`define 文でマクロを宣言します。用いるときにはバッククオートを忘れずにつけましょう。


参考にしたサイトなど

わからない〜ってなったときに度々お世話になったサイトさんを紹介させていただきます。

基本的にVerilogは情報が少ない気がします。 ためになるホームページさんは文字化けするので……(Googleのキャッシュから読み込めば読める裏技あり)

つよつよのひとにまとめてほしいです。

おわりに

気が向いたら追記していこうと思いますが、ひとまず公開します。

これから実験をやっていく人は私の屍を超えていってください