FC2カウンター FPGAの部屋 入門Verilog
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

1次元配列データのパートセレクト

OSERDESを使ってDDR2 SDRAMコントローラを作成しているが、その際に、どうしてもVerilogの構文がわからないところが出てきた。
OSERDESはDDRモードで4:1のシリアライザとして使用している。CLKDIV (100MHz, CLKは200MHz) 1クロックごとにOSERDESのD1, D2, D3, D4 のデータの4ビットとT1, T2, T3, T4 のトライステート状態ビット4ビット分を一度に入れる。DQSはSUZAKU-Vでは2ビットあるので、2ビット(2つ)分のデータ4ビットとトライステート状態4ビット分を確保したいと思った。1つ分ではないのは、ネットを短くして動作周波数を確保するためだ。
これを宣言するためには、2次元配列で宣言するのが一番自然なので、最初に2次元配列で宣言した。
だが、どうしても出力ポートを2次元配列にして上のモジュールに渡そうとしてもエラーになってしまう。どうしてよいか困ったため、mixiのVeritak友の会で聞いてみたところ、たっくさんからお返事があった。やはり、ポートの宣言に2次元配列は書けないということなので、1次元配列でやることにした。それでためしに自分で作ってみたのが下。

    output reg [4*DDR2_DQS_DM_WIDTH-1 : 0] dqs_oserdes_d_1d;
    output reg [4*DDR2_DQS_DM_WIDTH-1 : 0] dqs_oserdes_t_1d;

    always @(posedge clk) begin : DQS_OSERDES_1D // dqs_oserdes_d_1dとdqs_oserdes_t_1dがOSERDESに入力される
        integer k;
        
        for(k=0; k<=DDR2_DQS_DM_WIDTH-1; k=k+1) begin // 8bitに1個インスタンシエーション
             if (reset) begin
                 dqs_oserdes_d_1d[k*4+3 : k*4] <= 4'bXXXX;
                 dqs_oserdes_t_1d[k*4+3 : k*4] <= 4'b1111;
             end else begin
                 dqs_oserdes_d_1d[k*4+3 : k*4] <= dqs_oserdes_d;
                 dqs_oserdes_t_1d[k*4+3 : k*4] <= dqs_oserdes_t;
             end        
        end
    end


でもこれではエラーになってしまって、XSTの論理合成が通らない。(とりあえず、インプリメントできるかやっているので)
Veritak友の会で、べりろじしゃんという方に教えていただいて、2重ループで書いてみたらOKだった。

    always @(posedge clk) begin : DQS_OSERDES_1D // dqs_oserdes_d_1dとdqs_oserdes_t_1dがOSERDESに入力される
    integer k, m;

        for(k=0; k<=DDR2_DQS_DM_WIDTH-1; k=k+1) begin // 8bitに1個インスタンシエーション
            for(m=0; m<4; m=m+1) begin
                if (reset) begin
                    dqs_oserdes_d_1d[k*4+m] <= 1'bX;
                    dqs_oserdes_t_1d[k*4+m] <= 1'b1;
                end else begin
                    dqs_oserdes_d_1d[k*4+m] <= dqs_oserdes_d[m];
                    dqs_oserdes_t_1d[k*4+m] <= dqs_oserdes_t[m];
                end
            end
        end
    end



また、違う書き方を、たっくさんに教えていただいた。それによるとVerilog2001では下のように書けるそうだ。

    always @(posedge clk) begin : DQS_OSERDES_1D // dqs_oserdes_d_1dとdqs_oserdes_t_1dがOSERDESに入力される
        integer k, m;
        
        for(k=0; k<=DDR2_DQS_DM_WIDTH-1; k=k+1) begin // 8bitに1個インスタンシエーション
            if (reset) begin
                dqs_oserdes_d_1d[k*4 +: 4] <= 4'bXXXX;
                dqs_oserdes_t_1d[k*4 +: 4] <= 4'b1111;
            end else begin
                dqs_oserdes_d_1d[k*4 +: 4] <= dqs_oserdes_d;
                dqs_oserdes_t_1d[k*4 +: 4] <= dqs_oserdes_t;
            end
        end 
    end


下にたっくさんの書き込みを引用させていただきます。

reg [8:0] a;

のとき、

a[1 +:4] は、a[4:1] に等価です。1から出発して1,2,3,4 のパートセレクト
a[5 -:4: は、a[5:2] に等価です。5から出発して5,4,3,2のパートセレクト


ちなみに
reg [0:8] a;
のとき、
a[1 +: 4] は、a[1:4]に等価、a[5 -: 4] はa[2:5] に等価だそうだ。
たっくさん、貴重な知識を教えていただいてありがとうございました。

次に、べろりじしゃんさんにもgenerate を使用した書き方の例を教えていただいた。その例を参考に自分で書き直してみたのが下。

    generate
    genvar k, m;
        for(k=0; k<=DDR2_DQS_DM_WIDTH-1; k=k+1) begin : OSERDES_NO // 8bitに1個インスタンシエーション
            for(m=0; m<4; m=m+1) begin : OSERDES_PIN_NO
                always @(posedge clk) begin
                    if (reset) begin
                        dqs_oserdes_d_1d[k*4+m] <= 1'bX;
                        dqs_oserdes_t_1d[k*4+m] <= 1'b1;
                    end else begin
                        dqs_oserdes_d_1d[k*4+m] <= dqs_oserdes_d[m];
                        dqs_oserdes_t_1d[k*4+m] <= dqs_oserdes_t[m];
                    end
                end
            end
        end
    endgenerate


べろりじしゃんさん、貴重なVerilogコードを開示していただいてありがとうございます。

べろりじしゃんさんは、CQ出版でVerilog関連の本を何冊も書いている方のようだ。
  1. 2008年07月11日 21:18 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Verilog HDLでの数値リテラル(定数)の書き方

昨日は、PCI-Expressのセミナに行ってきてとてもためになった。講師のかたありがとうございました。

さて、”Verilog版DDR SDRAMコントローラのバグフィックス”の”wp, rpと演算する数のビット幅を制限, ビット幅の制限がないと特に-の演算をする時に符号拡張が32ビットで行われるためにfullが出力されない”というバグについて、今後の戒めとするために詳しく書いておこうと思う。
DDR SDRAMコントローラのアクセスするアドレスを入力しておくaddr_fifo.vではルックアップテーブルを使用した分散RAMを使用して、自分でFIFOを構成している。コアジェネレータを使用せずに自分でFIFOを書いている主な理由は、次のアドレスも出力したいからだった。つまり出力アドレスと次に出力するアドレス(入っていれば?)を出力して、BANKやROWアドレスが違っているときにプリチャージ、ACTコマンドを入れるようにしようという目論見でそうなった。コアジェネレータで作ると当然ながらデータ出力は当然1つなのでニーズに合わない。
分散RAMを使用してFIFOにするために、ライトポインタ(wp)とリードポインタ(rp)を定義してある。どちらのポインタともに4ビットとして定義している。FIFOのFULLを判定する式はrp-1 == wp となるのでそのまま下のように書いた。

assign fifo_full = (rp-1==wp) ? 1'b1: 1'b0;


でも、少なくともシミュレータでは危ない記述だそうだ。
なぜだめかというと上の記述で-1はビット幅を指定していないので、32ビット幅に拡張されてしまうそうだ。つまり32'hFFFF_FFFFになってしまう。左辺は当然32ビット符号拡張されるが、== の比較のときに右辺も32ビットに拡張されてしまう。
これでも通常の場合はOKだが、rpが0、wpがFの時(ポインタは4ビットに定義してあるので)当然ながらFIFOはFULLなのだが、左辺は32'hFFFF_FFFF、右辺は32'h0000_000Fとなってイコールにならないので、FULL信号がアクティブにならなかった。
これを避けるためにはrp-1の-1をビット幅を制限する必要があるそうだ。ここではポインタは4ビットなので、4ビットに制限する。つまり1は4'b0001か4'h1と書く必要がある。よって下のように書き換えたらFULL信号が正常に出力されるようなった。

assign fifo_full = (rp-4'h1==wp) ? 1'b1: 1'b0;


たっくさん、教えていただいてありがとうございます。
今度から、かならず数値リテラルもビット幅を指定することにしようと思う。
  1. 2008年03月28日 05:48 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:3

XilinxデバイスのVerilogシミュレーション時の注意点(glbl.v)

XilinxデバイスのVerilogシミュレーション時には、glbl.vをコンパイルする必要がある。glbl.v モジュールは、デザインのグローバル セット/リセット信号とグローバル トライステート信号を接続するそうである。
今回のシミュレーションでは、グローバル セット/リセット信号が100ns アサートされているのを忘れてしまい、何の変化もないと誤解してしまった。
XilinxデバイスのVerilogシミュレーション時には、intialから100ns waitしてから、信号を変化させる必要がある。
こんな感じ。

`timescale 1ps/1ps

...........

    initial begin // 10bitを出力
        #100000;    // GSRリセットを待つ
        #(PERIOD*2);
        rst = 1'b0;


今日の教訓でした。
  1. 2007年11月26日 21:13 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Xilinxのライブラリをコンパイルする場合の注意(`default_nettype wire)

私は、Verilogを書くときにwire宣言をしないとエラーにするために、Verilogコードの最初に必ず`default_nettype noneを書くが、この設定で、ほかの暗黙のwire宣言を使っているその他のVerilogコードをコンパイルするとエラーになってしまう。
そのためにテストベンチの最後に必ず`default_nettype wireを書いておく。

endmodule
`default_nettype wire


なお、ModelSimでは、コンパイル済みのライブラリを使っているので、こう書く必要はないが、Veritakではライブラリもコンパイルしているので、必要のようだ。ということはVeritakはライブラリのVerilogファイルのコンパイルは最後ということになるのかな?
  1. 2007年11月26日 06:20 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:2

SystemVerilogセミナ(アサーションコース)

2007/12/13(木)~2007/12/14(金)に、HDラボ(株)のSystemVerilogセミナ(アサーションコース)に申し込みました。
LINUXで1人1台のPCというのが、ちょっと気になりますが、行って見ることにしました。EMACSあまり好きじゃないんで。。。コントロールキーを押しながら、ほかのキーをブラインドタッチで押せない。。。
VIでも良いんでしょうか?
QuestaSim 6.1bはModelSimみたいな物だから問題ないとして。。。
後は、新横浜まで通うのが大変そう。新幹線で行こうかな?行くだけは。。。帰りは普通ということで。。。そういえば、JR東海だからスイカカード使えないという話ですね?ということは秋葉原で切符と特急券を買ったほうが良い?
何はともあれ、有意義な研修になるようにしようと思います。
  1. 2007年11月11日 15:38 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:4

PS/2キーボードインターフェース用テストベンチ(task使用)

PS/2キーボードインターフェース用テストベンチ(procedure使用)でVHDLの procedure 文を使って、PS/2キーボードインターフェースをシミュレーションしてみた。
今度はVerilog2001で task 文を使ってシミュレーションしてみた。task もprocedure 同様サブプログラムとして使用できるようだ。Verilog は、VHDLと違って、出力信号を定義せずに直接信号を変更できるようだ。これは私としては、信号がグローバルになっているようであまり好きではない。便利なことはいろいろあるのだけど。。。
ps2read.v は、日曜デジタルさんのコードを丸写しで直しても芸がないので、自分で作ってみた。ずっとコード量は多いし、回路も複雑になると思うが、シリアルデータ受信のすべてのフェーズを1つ1つのステートにした。
さらにステートを見やすいように、MAIN_STATEにステート文字をアサインした。
Verilogを書くのは久しぶりなので、大分苦労してしまったが、何とか完成。

// ps2read Verilog2001

`default_nettype none
`timescale 1ns / 1ps

module ps2read(clk, reset, ps2clk, ps2data, scandata);
    input clk, reset, ps2clk, ps2data;
    output [7:0] scandata;
    
    wire clk, reset, ps2clk, ps2data;
    reg [7:0] scandata;
    reg [10:0] c_state, n_state;
    reg ps2clk_b1, ps2clk_b2;
    reg ps2data_b;
    reg [7:0] c_scandata, n_scandata;
    
    parameter IDLE=11'b00000000001, BIT0=11'b00000000010, BIT1=11'b00000000100, BIT2=11'b00000001000, BIT3=11'b00000010000, BIT4=11'b00000100000, BIT5=11'b00001000000, BIT6=11'b00010000000, BIT7=11'b00100000000, PARITY=11'b01000000000, STOP=11'b10000000000;
    
    always @(posedge reset, posedge clk) begin
        if (reset) begin
            ps2clk_b1 <= 1'b0;
            ps2clk_b2 <= 1'b0;
            ps2data_b <= 1'b0;
        end else begin
            ps2clk_b1 <= ps2clk;
            ps2clk_b2 <= ps2clk_b1;
            ps2data_b <= ps2data;
        end
    end
            
    always @(posedge reset, posedge clk) begin
        if (reset) begin
            c_state <= IDLE;
            c_scandata <= 0;
        end else begin
            c_state <= n_state;
            c_scandata <= n_scandata;
        end
    end
    
    always @* begin
        case (c_state)
            IDLE : begin
                n_scandata <= c_scandata;
                if (ps2clk_b2 && !ps2clk_b1)
                    n_state <= BIT0;
                else
                    n_state <= IDLE;
            end
            BIT0 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT1;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT0;
                    n_scandata <= c_scandata;
                end
            BIT1 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT2;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT1;
                    n_scandata <= c_scandata;
                end
            BIT2 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT3;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT2;
                    n_scandata <= c_scandata;
                end
            BIT3 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT4;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT3;
                    n_scandata <= c_scandata;
                end
            BIT4 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT5;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT4;
                    n_scandata <= c_scandata;
                end
            BIT5 : 
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT6;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT5;
                    n_scandata <= c_scandata;
                end
            BIT6 :
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= BIT7;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT6;
                    n_scandata <= c_scandata;
                end
            BIT7 :
                if (ps2clk_b2 && !ps2clk_b1) begin
                    n_state <= PARITY;
                    n_scandata <= {ps2data_b, c_scandata[7:1]};
                end else begin
                    n_state <= BIT7;
                    n_scandata <= c_scandata;
                end
            PARITY : begin
                n_scandata <= c_scandata;
                if (ps2clk_b2 && !ps2clk_b1)
                    n_state <= STOP;
                else
                    n_state <= PARITY;
            end
            STOP : begin
                n_scandata <= c_scandata;
                if (ps2clk_b2 && !ps2clk_b1)
                    n_state <= IDLE;
                else
                    n_state <= STOP;
            end
        endcase
    end
    
    always @(posedge reset, posedge clk) begin
        if (reset)
            scandata <= 0;
        else
            if (c_state == STOP)
                scandata <= c_scandata;
    end
    
    // synthesis translate_off

    reg [20*8:1] MAIN_STATE; 
    
    always @(c_state) begin
        case(c_state)
            IDLE: MAIN_STATE <= "IDLE";
            BIT0: MAIN_STATE <= "BIT0";
            BIT1: MAIN_STATE <= "BIT1";
            BIT2: MAIN_STATE <= "BIT2";
            BIT3: MAIN_STATE <= "BIT3";
            BIT4: MAIN_STATE <= "BIT4";
            BIT5: MAIN_STATE <= "BIT5";
            BIT6: MAIN_STATE <= "BIT6";
            BIT7: MAIN_STATE <= "BIT7";
            PARITY: MAIN_STATE <= "PARITY";
            STOP: MAIN_STATE <= "STOP";
        endcase
    end
    // synthesis translate_on
     
endmodule


次にテストベンチを作成した。task 文を使用してVHDL の procedure 文の時と同様に書いてみた。
PS/2のクロック間隔は、'1'の期間が40us、'0'の期間が40us のクロックと、VHDLと同じとした。task 文には入力8ビット幅のスキャンコードだけがアサインされている。出力は直接信号に出力される。

`default_nettype none
`timescale 1ns / 1ps

module ps2read_tb;

    parameter CYCLE=20;

    reg clk, reset, ps2clk, ps2data;
    wire [7:0] scandata;
    
    ps2read ps2read_inst (
        .clk(clk),
        .reset(reset),
        .ps2clk(ps2clk),
        .ps2data(ps2data),
        .scandata(scandata)
    );
    
    initial begin
        clk <= 1'b1;
    end
    always #(CYCLE/2)
        clk <= ~clk;
    
    initial begin
        reset <= 1'b1;
        ps2clk <= 1'b1;
        ps2data <= 1'b1;
        
        #80; // リセット
        reset <= 1'b0; // リセット解除
        PS2_SigGen(8'h1C);
        PS2_SigGen(8'h32);
        #500;
        $stop;
    end
    
    task PS2_SigGen;
        input [7:0] input_code;
        integer i;
        begin
            ps2clk <= 1'b1;
            ps2data <= 1'b0; // Stop bit
            
            #40000;
            ps2clk <= 1'b0; // 立ち下がりエッジ
            #40000;
            
            for(i=0; i<=7; i=i+1) begin
                ps2clk <= 1'b1; // 立ち上がりエッジ
                ps2data <= input_code[i]; // シリアルデータ出力
                
                #40000;
                ps2clk <= 1'b0; // 立下りエッジ
                #40000;
            end
        
            ps2clk <= 1'b1;
            ps2data <= !(^input_code);
        
            #40000;
            ps2clk <= 1'b0; // 立ち下がりエッジ
            #40000;
        
            ps2clk <= 1'b1;
            ps2data <= 1'b1; // Stop bit
        
            #40000;
            ps2clk <= 1'b0; // 立ち下がりエッジ
            #40000;
            ps2clk <= 1'b1; // 1に戻す
        end
    endtask
endmodule


ModelSimでシミュレーションしてみた。
wave表示で、MAIN_STATE表示するときには、そこを右クリックして、Radix -> ASCII を選択するとステート名で表示することができる。
keyboard_verilog_2_071008.png

さらにVeritak でも確かめてみた。
keyboard_verilog_1_071008.png


2007/10/13 : PS/2インターフェースのパリティが偶数パリティだったので、奇数パリティに変更しました。
2007/10/15 : ps2clkの1クロック遅延で比較していたので、ps2dataも1クロック遅延しました。ここでは、50MHzでサンプリングしているので大して変わりませんが、もっとサインプリングレートを低くしたときに効いてきます。
  1. 2007年10月08日 13:02 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Verilog2001でのネット名キープ方法

FPGAインフォメーションのBBSで論理合成で信号がなくなってしまうときに、XSTのKEEP属性を使用して、信号をキープする方法をポストした。
VHDLは今までやってきたのでOKだが、Verilog HDLはやったことがなかった。そこで推測というか、どうやるか興味があったので制約ガイドやアンサーサーチを見た。
その結果、”8.1i XST - Verilog コードで属性を受け渡す場合の推奨方法について”を見つけた。それによるとVerilog2001だと

(* IOSTANDARD="LVDCI_33" *) input rxd ;


のように書くそうだ。
そこで、Verilog2001版DDR SDRAMコントローラの addr_fifo.v の wire fifo_full; があるが、論理合成して Constraints Editor のNET検索で見ると fifo_full はネットがなくなっている。
Verilog2001_KEEP_Const_1_070301.png

これで wire fifo_full; を下のように変えると、下のスナップショットのように論理合成後にネット名が残った。

(* KEEP="TURE" *) wire fifo_full;


Verilog2001_KEEP_Const_2_070301.png

  1. 2007年03月01日 22:30 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0