たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

僕的SystemVerilogメモ

絶望の証

何も見えない

進捗は出ないけど知見が増えたので SystemVerilogが一ミリもわからない人向け

Queueは論理合成できない

module Test();
    byte q[$]; //<--できない
    
    initial begin
        q.push_back(12); //<--できない
    end
endmodule

SystemVerilogのQueueはQueueというより双方向Queueで、C++の感覚で使えるので便利だと思ってわくわくしてたけど現行のQuartus Prime 17.0でも論理合成できない。

同じくわくわくしてた連想配列も論理合成できない

//KeyがbyteでValueがintの連想配列
int dic[byte]; // <--できない

structとenumは論理合成できる

でもstructとenumは論理合成できる。enumは値に名前を付けれて便利 enumはデータ型を指定できる。以下はbyteを指定した例

typedef enum byte {TINO=0,KOKOA=1,RIZE=2} RABBITHOUSE;

enum のあとを省略すると32bit?(たぶんint)になる。
さらに代入を省略すると0からの連番になる、先頭に値を 指定するとそこからの連番になる

//zero=0から始まる連番が順番に割り当てになる
typedef enum {zero,one,two,three,four} num;

SystemVerilogのenumはスコープがない・・・というか全部グローバルに宣言される感じ
なので使うときは名前をそのまま書けば使える

typedef enum byte {TINO=3,KOKOA,RIZE} RABITHOUSE;

initial begin
    $display("Data : ",KOKOA); //<-- Data : 4って出力される
end

名前が一緒だったら既にあるっておこられる

typedef enum {TINO} TINOCHAN;
typedef enum {TINO,KOKOA,RIZE} RABBITHOUSE; //<--既にTINOが定義されてるので死ぬ

structも使える

typedef struct packed {
    byte x;
    byte y;
} Vector2;

Vector2 pos; //<--宣言するときはこう

byte tmp=pos.x; //<--xを読み出したいときはこう

初期化?もC++みたいにかける。

Vector2 pos={8'h01,8'h02}; //<-- xに1 yに2

pragma once

インクルードするときC言語みたいに多重定義すると死ぬ
これを書いてる間にSystemVerilogにはC++みたいにpragma onceを指定すれば1度だけインクルードされて多重定義を防げる

//test.sv
`pragma once
module test();
    dark zone
endmodule

//second.sv
`include "test.sv"
modeule second();
    second dark zone
endmodule

//top.sv
`include "test.sv"
`include "second.sv"
module top();
    天衣無縫
endmodule

test.svpragma onceがないとsecond.svの解析中に多重定義で死亡する

これを見つけるまではifdef-else define -endifでやってた。バカス

2017年11月15日追記

なんかやっぱりpragma once はダメだったのでこれは無かったことに
僕が見たのは幻覚か

一回だけいんくるするならやっぱりifdefを使う

`ifdef REG_ONCE
`else
`define REG_ONCE

module register();
endmodule

`endif

これでめでたく解決!(長くてめんどい)

automatic

SystemVerilogは急に変数を宣言できない。回路はstaticだから
変数宣言するならModuleの先頭かinitialブロックの中でしかできない。Moduleの先頭で宣言するとstaticな変数になる。アクセスはModule内ならどこからでもできる しかしコードを書いてると急に変数を宣言したくなるでしょ
そういう時はautomaticキーワードを使えば動的な変数が定義できる

module test();
    byte abc;//<--これはstatic test内ならどこからでもアクセスできる
    
    initial begin
        byte k; //<--initialの中ではOKでもスコープはこのブロックの中だけ
    end
    
    function void abc();
        byte abc=0; //<--これはダメ
        automatic byte edf=0; //<--これはOK
        edf=123; //なんか処理かく
        automatic byte cc=0; //<--これはダメ、automaticをつかって宣言しても結局スコープの先頭でしかできない        
    endfunction

    //automaticを使わないといけないのはtaskやalwaysも
endmodule

変数の種類

japan.xilinx.comhttps://japan.xilinx.com/support/answers/51327.html
ここが詳しい

書いててたまに不便だと思うのがbyteとかが符号付きなところ
二進数世界なのに符号付とかいみわかんなくない?でもプログラム書くとき的には普通じゃないってなってて板挟み

符号なしの多bit幅型を使うときはlogic[15:0]とかって書けば16bitの型が書けるけどこれはいちいち面倒なのでよく使うのはtypedefすればいいと思う

//16bit型
typedef logic[15:0] d_byte;
//ちなみにunsignedを付ければ符号なしにできる
typedef byte unsigned ubyte;

typedefはmoduleの外とかでもできるから全体的によく使うやつとかは別ファイルにまとめて使うときにincludeすると便利かもね。

interface

よく使うポートとかをまとめられる神機能。しかも論理合成できる よくわかんないだろうから例

interface clk_rst;
    bit clk;
    bit rst;
endinterface

module top(clk_rst c);
        // ↑こんな風にclkとrstをまとめられる
    
    always @(posedge c.clk) begin //<--アクセスはstructを同じようにやる
    
    end
endmodule

fork-joinブロック

taskをforkできる sites.google.com
ここが詳しい

eventforeverと組み合わせるとalwaysの代わりが作れるけどこのfork-joinブロックは論理合成できない残念!

これが役に立つと個人的に思うのはテストベンチでクロックを無限に出したいとき

module TB();
    bit clk,rst,endofrun;
    Test test(.*);
    
    task clkgen();
        while(!endofrun)begin
            #50 clk<=0;
            #50 clk<=1;
        end
    endtask
    
    initial begin
        fork begin
            clkgen();
        join_none
        
        //~~~なんか処理
    end
endmodule

このテクニックはネットのどこかで見たから使ってる。どこだったか忘れたので出典はかけない・・・ごめんなさい

まだ何個かある気がするけど思い出すも書くのも疲れたのでここまで
気が向いたらまた書くかもしれないし、マサカリもお願いしたい