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

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

FPGAの部屋

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

ある parameter の値で、他の parameter の値を切り替える(Verilog編)

画像を表示するIPをXPSプロジェクトにインスタンス化する時に、paramterにVGA、SVGA、XGA を選んで、画像の解像度を切り替えることができると、とっても便利です。そこで、あるparameter値で、その他のparameter値を切り替えることができるかどうかやってみました。(出来ました)

Verilog HDLのソースコードです。

// parameter_test.v

module parameter_test # (
    parameter RESOLUTION = "VGA"
)(
    output wire [10:0] param_out
);

parameter integer H_ACTIVE_VIDEO = (RESOLUTION=="VGA") ? 640 :
                    (RESOLUTION=="SVGA") ?    800 :
                    (RESOLUTION=="XGA") ?    1024 :
                    (RESOLUTION=="SXGA") ?    1280 : 
                    (RESOLUTION=="HD") ?    1920 : 1920;

    assign param_out = H_ACTIVE_VIDEO;
endmodule


RESOLUTIONパラメータの文字列で、H_ACTIVE_VIDEOのパラメータ値を切り替えています。
ISE14.7でプロジェクトを作ってみました。
param_test_1_140304.png

ISimでシミュレーションしてみたところ、想定していた動作になりました。
param_test_2_140304.png

parameter RESOLUTION = "XGA"

にしてみました。
param_test_3_140304.png

問題なさそうです。これを使おうと思います。
  1. 2014年03月04日 08:59 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

XPSプロジェクトのカスタムIP内のgenerateで生成したメモリの初期値を設定する(Verilog HDL)

現在、Vivado HLS 2013.4で生成したラプラシアンフィルタのカスタムIPをシミュレーションしようとしている。”Vivado HLS 2013.4でラプラシアン・フィルタ関数をaxi masterモジュールにする4”参照

ISEでシミュレーション用のプロジェクトを作製し、XPSプロジェクトをISEプロジェクト内に作製した。
Vivado_HLS_2013_4_16_140207.png

XPSプロジェクトがこちら。lap_filter_axim_top_0 の M_AXI_CAM_FB, M_AXI_LAP_FB がメモリモデルの mem_sim_axi_slave_0 に接続されている。
Vivado_HLS_2013_4_17_140207.png

メモリモデルの mem_sim_slave については、”AXI4 Slave インターフェースのメモリ・シミュレーション用 IP の作製2(シミュレーション)”を参照のこと。C_MEMORY_SIZE = 960000 に設定している。800x600ピクセルのSVGA画面2画面分だ。

2画面分の領域を確保したうちの1画面だけを画像の情報として初期化したいという欲求があった。画像データをファイルから読み込んで、そのまま使用してもよいのだが、面倒なので、おなじみのM系列の擬似ランダム数で初期化することにした。

いろいろと試行錯誤してしまったが、出来たのでやり方を書いておく。階層としては、次の通りになる。

1.system_top_tbがシミュレーションのテストベンチで最上位ということになる。

2.その下に、system_topを uut してインスタンスしてある。

3.systemという名前のXPSプロジェクトが、system_i という名前でインスタンスしてある。

4.XPSプロジェクトに下に、 mem_sim_axi_slave_0をインスタンスしてある。

5.mem_sim_axi_slaveでは、memory_8bit を memory_8bit_i というインスタンス名で generate 文でインスタンスしてある。


mem_sim_axi_slave.v 上で、memory_8bit をインスタンスしてある部分を下に示す。

    // instance memory_8bit
    generate
        genvar i;
        
        for (i=(C_S_AXI_DATA_WIDTH/8-1); i>=0; i=i-1) begin : MEMORY_GEN
            memory_8bit #(
                .C_S_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH),
                .C_MEMORY_SIZE(C_MEMORY_SIZE)
            ) memory_8bit_i (
                .clk(ACLK),
                .waddr(waddr),
                .write_data(S_AXI_WDATA[i*8+7:i*8]),
                .write_enable(wready & S_AXI_WVALID),
                .byte_enable(S_AXI_WSTRB[i]),
                .raddr(raddr),
                .read_data(S_AXI_RDATA[i*8+7:i*8])
            );
        end
    endgenerate


この mem にM系列の擬似ランダム数を入れたい。下にテストベンチでのM系列の生成するためのfunction を示す。

    // R, G, B 毎に違った生成多項式のM系列を用意した
    function [7:0] mseqf8_R (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_R = {din[6:0], xor_result};
        end
    endfunction

    function [7:0] mseqf8_G (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[4] ^ din[2] ^ din[0];
            mseqf8_G = {din[6:0], xor_result};
        end
    endfunction

    function [7:0] mseqf8_B (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[5] ^ din[2] ^ din[1];
            mseqf8_B = {din[6:0], xor_result};
        end
    endfunction


下に、初期化を行った Verilog HDL のコードを示す。これは、テストベンチ system_top_tb.v から、XPSプロジェクト以下の メモリの前半分を初期化している。

    // M系列を使用した擬似ランダム数でメモリの半分を初期化した
    initial begin : memory_init_zero
        integer i;
        for (i=0; i<480000; i=i+1) begin
            uut.system_i.mem_sim_axi_slave_0.mem_sim_axi_slave_0.MEMORY_GEN[3].memory_8bit_i.mem[i] = 8'd0;
        end
    end

    initial begin : memory_init_red
        integer i;
        for (i=0; i<480000; i=i+1) begin
            uut.system_i.mem_sim_axi_slave_0.mem_sim_axi_slave_0.MEMORY_GEN[2].memory_8bit_i.mem[i] = mseq8r;
            mseq8r = mseqf8_R(mseq8r);
        end
    end

    initial begin : memory_init_green
        integer i;
        for (i=0; i<480000; i=i+1) begin
            uut.system_i.mem_sim_axi_slave_0.mem_sim_axi_slave_0.MEMORY_GEN[1].memory_8bit_i.mem[i] = mseq8g;
            mseq8g = mseqf8_G(mseq8g);
        end
    end

    initial begin : memory_init_blue
        integer i;
        for (i=0; i<480000; i=i+1) begin
            uut.system_i.mem_sim_axi_slave_0.mem_sim_axi_slave_0.MEMORY_GEN[0].memory_8bit_i.mem[i] = mseq8b;
            mseq8b = mseqf8_B(mseq8b);
        end
    end


ISim のメモリ表示を下に示す。
Vivado_HLS_2013_4_18_140207.png

メモリがM系列の擬似ランダム数で初期化されているのがわかった。


注意が必要なのは、XPSプロジェクトでは、mem_sim_axi_slave_0 の下の mem_sim_axi_slave_0 に実体があるということだ。それに、generate 文を使った時のインスタンス名に注意して欲しい。(XilinxのXSTを使用した時のインスタンスの仕方です。Altera の Quartus II では、インスタンス名が違うかもしれません?)
  1. 2014年02月07日 04:39 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Verilog HDL で unsigned, signed の演算をする2(実践編)

”Verilog HDL で unsigned, signed の演算をする1”の続き。

ISE14.7 の Project Navigator でプロジェクトを作って、確かめた。

下にProject Navigator のプロジェクトを示す。
Verilog_Signed_1_131205.png

下に、Verilog HDLの calculation_test.v を示す。

`default_nettype none

module calculation_test(

    input    wire            [7:0]    a,
    input    wire            [7:0]    b,
    input    wire    signed    [7:0]    sc,
    input    wire    signed    [7:0]    sd,
    input   wire            [7:0]   e,
    input   wire    signed  [7:0]   sf,

    output    wire            [7:0]    x1,
    output    wire            [8:0]    x2,
    output    wire    signed    [8:0]    sy1,
    output    wire    signed    [8:0]    sy2,
    output  wire    signed  [8:0]   sz1,
    output  wire    signed  [8:0]   sz2
);
    // unsigned - unsigned
    assign    x1 = a - b;
    assign    x2 = {1'b0, a} - {1'b0, b};    // ビット幅を合わせた

    // signed - signed
    assign    sy1 = sc - sd;
    assign    sy2 = {sc[7], sc} - {sd[7], sd};    // 明示的に符号拡張

    // signed - $signed(unsigned)
    assign  sz1 = sf - $signed(e);
    assign  sz2 = sf - $signed({1'b0, e[6:0]});
endmodule


下に、テストベンチの calculation_test_tb.v を示す。

`timescale 1ns / 1ps

module calculation_test_tb;

    parameter    DELAY =    10;

    // Inputs
    reg             [7:0] a;
    reg             [7:0] b;
    reg        signed    [7:0] sc;
    reg     signed    [7:0] sd;
    reg             [7:0] e;
    reg     signed    [7:0] sf;

    // Outputs
    wire             [7:0] x1;
    wire             [8:0] x2;
    wire    signed    [8:0] sy1;
    wire     signed    [8:0] sy2;
    wire     signed    [8:0] sz1;
    wire     signed    [8:0] sz2;

    // Instantiate the Unit Under Test (UUT)
    calculation_test uut (
        .a(a),
        .b(b),
        .sc(sc),
        .sd(sd),
        .e(e),
        .sf(sf),
        .x1(x1),
        .x2(x2),
        .sy1(sy1),
        .sy2(sy2),
        .sz1(sz1),
        .sz2(sz2)
    );

    initial begin
        // Initialize Inputs
        a = 0;
        b = 0;
        sc = 0;
        sd = 0;
        e = 0;
        sf = 0;

        // Wait 100 ns for global reset to finish
        #100;

        // Add stimulus here
        a = 3; b = 2;
        #DELAY;
        $display("x = a - b");
        if (x1>=0)
            $display("a=%d, b=%d, x1=%d, x1>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x1=%d, x1<0", a, b,x1);
        if (x2>=0)
            $display("a=%d, b=%d, x2=%d, x2>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x2=%d, x2<0", a, b,x1);

        sc = 3; sd = 2;
        #DELAY;
        $display("sy = sc - sd");
        if (sy1>=0)
            $display("sc=%d, sd=%d, sy1=%d, sy1>=0", sc, sd, sy1);
        else
            $display("sc=%d, sd=%d, sy1=%d, sy1<0", sc, sd, sy1);
        if (sy2>=0)
            $display("sc=%d, sd=%d, sy2=%d, sy2>=0", sc, sd, sy2);
        else
            $display("sc=%d, sd=%d, sy2=%d, sy2<0", sc, sd, sy2);

        sf = 3; e = 2;
        #DELAY;
        $display("sz = sf - e");
        if (sz1>=0)
            $display("sf=%d, e=%d, sz1=%d, sz1>=0", sf, e, sz1);
        else
            $display("sf=%d, e=%d, sz1=%d, sz1<0", sf, e, sz1);
        if (sz2>=0)
            $display("sf=%d, e=%d, sz2=%d, sz2>=0", sf, e, sz2);
        else
            $display("sf=%d, e=%d, sz2=%d, sz2<0", sf, e, sz2);

        $display("");

        a = 2; b = 3;
        #DELAY;
        $display("x = a - b");
        if (x1>=0)
            $display("a=%d, b=%d, x1=%d, x1>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x1=%d, x1<0", a, b,x1);
        if (x2>=0)
            $display("a=%d, b=%d, x2=%d, x2>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x2=%d, x2<0", a, b,x1);

        sc = 2; sd = 3;
        #DELAY;
        $display("sy = sc - sd");
        if (sy1>=0)
            $display("sc=%d, sd=%d, sy1=%d, sy1>=0", sc, sd, sy1);
        else
            $display("sc=%d, sd=%d, sy1=%d, sy1<0", sc, sd, sy1);
        if (sy2>=0)
            $display("sc=%d, sd=%d, sy2=%d, sy2>=0", sc, sd, sy2);
        else
            $display("sc=%d, sd=%d, sy2=%d, sy2<0", sc, sd, sy2);

        sf = 2; e = 3;
        #DELAY;
        $display("sz = sf - e");
        if (sz1>=0)
            $display("sf=%d, e=%d, sz1=%d, sz1>=0", sf, e, sz1);
        else
            $display("sf=%d, e=%d, sz1=%d, sz1<0", sf, e, sz1);
        if (sz2>=0)
            $display("sf=%d, e=%d, sz2=%d, sz2>=0", sf, e, sz2);
        else
            $display("sf=%d, e=%d, sz2=%d, sz2<0", sf, e, sz2);

        $display("");

        a = -128; b = 128;
        #DELAY;
        $display("x = a - b");
        if (x1>=0)
            $display("a=%d, b=%d, x1=%d, x1>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x1=%d, x1<0", a, b,x1);
        if (x2>=0)
            $display("a=%d, b=%d, x2=%d, x2>=0", a, b,x1);
        else
            $display("a=%d, b=%d, x2=%d, x2<0", a, b,x1);

        sc = -128; sd = 128;
        #DELAY;
        $display("sy = sc - sd");
        if (sy1>=0)
            $display("sc=%d, sd=%d, sy1=%d, sy1>=0", sc, sd, sy1);
        else
            $display("sc=%d, sd=%d, sy1=%d, sy1<0", sc, sd, sy1);
        if (sy2>=0)
            $display("sc=%d, sd=%d, sy2=%d, sy2>=0", sc, sd, sy2);
        else
            $display("sc=%d, sd=%d, sy2=%d, sy2<0", sc, sd, sy2);

        sf = -128; e = 127;
        #DELAY;
        $display("sz = sf - e");
        if (sz1>=0)
            $display("sf=%d, e=%d, sz1=%d, sz1>=0", sf, e, sz1);
        else
            $display("sf=%d, e=%d, sz1=%d, sz1<0", sf, e, sz1);
        if (sz2>=0)
            $display("sf=%d, e=%d, sz2=%d, sz2>=0", sf, e, sz2);
        else
            $display("sf=%d, e=%d, sz2=%d, sz2<0", sf, e, sz2);
    end

endmodule


これらの Verilog HDL ファイルを ISim でコンパイルしたところ以下の出力が得られた。

x = a - b
a= 3, b= 2, x1= 1, x1>=0
a= 3, b= 2, x2= 1, x2>=0
sy = sc - sd
sc= 3, sd= 2, sy1= 1, sy1>=0
sc= 3, sd= 2, sy2= 1, sy2>=0
sz = sf - e
sf= 3, e= 2, sz1= 1, sz1>=0
sf= 3, e= 2, sz2= 1, sz2>=0

x = a - b
a= 2, b= 3, x1=255, x1>=0
a= 2, b= 3, x2=255, x2>=0
sy = sc - sd
sc= 2, sd= 3, sy1= -1, sy1<0
sc= 2, sd= 3, sy2= -1, sy2<0
sz = sf - e
sf= 2, e= 3, sz1= -1, sz1<0
sf= 2, e= 3, sz2= -1, sz2<0

x = a - b
a=128, b=128, x1= 0, x1>=0
a=128, b=128, x2= 0, x2>=0
sy = sc - sd
sc=-128, sd=-128, sy1= 0, sy1>=0
sc=-128, sd=-128, sy2= 0, sy2>=0
sz = sf - e
sf=-128, e=127, sz1=-255, sz1<0
sf=-128, e=127, sz2=-255, sz2<0


最後の sc に 128 を入れて演算している例では、128は8ビットの符号付きの数値として表すことはできないが、integer の値をそのまま代入して計算されているので、常にデータのビット幅について注意が必要のようだ。
sc = 127とすれば、正常に -255 が計算された。

下に、ISim のタイミング波形を示す。
Verilog_Signed_2_131205.png

これで、シミュレーションは終了した。Verilog HDLでも signed を使うことができそうだ。

次は、論理合成、インプリメントを行った。成功した。FPGAはZedBoardのZynqだ。
Verilog_Signed_3_131205.png

RTL View で見ると、x1とx2は統合されていたが、他は別々に実装してあった。
Verilog_Signed_4_131205.png
Verilog_Signed_5_131205.png

FPGA Editor でPlace and Route 後の結果を見ると、sy2も削除されているようだ。
Verilog_Signed_6_131205.png

次に、Post-Map Simulation Model を生成した。
Verilog_Signed_7_131205.png

これで、Post-Map Simulationを実行することができる。Simulation モードで、Post-Map を指定してシミュレーションを行った。
Verilog_Signed_8_131205.png

シミュレーション結果を下に示す。
Verilog_Signed_9_131205.png

シミュレーションの出力は、Behavioral Simulation の結果と同様だった。

ちなみに、Post-Route のシミュレーション出力は違っていた。遅延の影響だと思われる。遅延を計算して、テストベンチを作製する必要があるようだ。
  1. 2013年12月05日 05:24 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Verilog HDL で unsigned, signed の演算をする1

今までは、厳密な言語仕様のVHDLで演算をしてきた。math_realパッケージをuse すれば、log2 も sin() も cos() も使用することができる。

例えば、”LOG2の値の求め方2”でスタティックなlog2の値 をreal で求めた。

VHDLのmath_realパッケージを使用した sin() と cos() の計算”では、スタティックな sin()もcos()の値を math_realパッケージを使用して求めた。

VHDLでは、”ポートを配列で宣言して下位階層に配線を接続する”事もできる。

それで、演算を本格的に使う場合は、VHDLを使って来たが、Verilog HDL も簡単な演算は使ってみようということでやってみることにした。

まずは、Verilog HDL は普通に宣言すると unsigned で、signed にする場合は、特別に signed を付けるとのことだ。(初めてでも使えるVerilog HDL文法ガイド ―― 記述スタイル編

wire        [31:0] data;
wire signed [31:0] data;


Verilog HDLでsinged と unsigned の演算の取り扱いの違いをツイッターで聞いてみた。(ツイッターのログです。@KSuzukii さん、@tkuro11 さんありがとうございました。問題がありましたら、お知らせ下さい)
比較演算子の取り扱いが気になって聞いてみました。signed になると最上位ビットが負の符号になって、大小関係が変わるはずだ。(2の補数)
通常に宣言した unsigned を signed にキャストする時は、最上位ビットに 1'b0 を付け加えて1ビット増やしてから signed にキャストするのが一般的だと思う。

signed, unsigned 演算については、元大学院生の日記さんの
[メモ]verilogのsigned演算をいろいろ試してみた”と
[メモ]verilogでsignedとunsignedの乗算
に詳しく載っていた。
signed キャストは、$signed()を使うそうだ。signed で宣言された data3 に unsigned で宣言された data1, data2の引き算の値を代入するには下の式を使うようだ。

assign data3 = $signed({1'b0, data1}) - $signed({1'b0, data2});


次は、実際に自分でXilinxのツールを使って演算をしてみたいと思う。
  1. 2013年12月03日 05:16 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:0

Verilog HDLでの log2 の求め方(Constant Functions in Verilog 2001)

Beyond Circuits は、Synchronous FIFO や LIFO などのXSTでの書き方が書いてある有用なWebページだ。

そこの、Constant Functions in Verilog 2001 を見ると、Verilog HDLによる log2 の求め方が書いてあった。

Constant Function とは、コンパイル時に値が決まる定数のFunction をビット幅の宣言時などに呼び出して、ビット幅を決定できる関数のことである。つまり、定数値をコンパイル時に決定するために使用し、実際の回路にはならない関数のことだ。これで log2 の演算を定義すると、ビット幅をメモリサイズから定義できるので、とっても便利だ。

Constant Functions in Verilog 2001 の記述を参照して、メモリ・モジュールを書いてみた。インプリメントするとBlockRAMにアサインされると思うが未確認だ。下に示す。(2013/06/26:変更)(2013/07/03:バグ修正)

// 8bit Memory Module

`default_nettype none

module memory_8bit #(
    parameter integer C_S_AXI_ADDR_WIDTH            = 32,
    parameter integer C_MEMORY_SIZE                    = 512    // Word (not byte)
)(
    input    wire    clk,
    input    wire    [C_S_AXI_ADDR_WIDTH-1:0]    waddr,
    input    wire    [7:0]    write_data,
    input    wire    write_enable,
    input    wire    byte_enable,
    input    wire    [C_S_AXI_ADDR_WIDTH-1:0]    raddr,
    output    reg        [7:0]    read_data
);

    // Beyond Circuts, Constant Function in Verilog 2001を参照しました
    // http://www.beyond-circuits.com/wordpress/2008/11/constant-functions/
    function integer log2;
        input integer addr;
        begin
            addr = addr - 1;
            for (log2=0; addr>0; log2=log2+1)
                addr = addr >> 1;
        end
    endfunction
    
    reg        [7:0]    mem    [0:C_MEMORY_SIZE-1];
    wire    [log2(C_MEMORY_SIZE)-1:0]    mem_waddr;
    wire    [log2(C_MEMORY_SIZE)-1:0]    mem_raddr;
    
    // The Address is byte address
    assign mem_waddr = waddr[(log2(C_MEMORY_SIZE)+log2((C_S_AXI_ADDR_WIDTH/8)))-1:log2((C_S_AXI_ADDR_WIDTH/8))];
    assign mem_raddr = raddr[(log2(C_MEMORY_SIZE)+log2((C_S_AXI_ADDR_WIDTH/8)))-1:log2((C_S_AXI_ADDR_WIDTH/8))];
    
    // Write
    always @(posedge clk) begin
        if (write_enable & byte_enable)
            mem[mem_waddr] <= write_data;
    end
    
    // Read
    always @(posedge clk) begin
        read_data <= mem[mem_raddr];
    end
endmodule

`default_nettype wire


(追加)
AR# 44586 13.2 Verilog $clog2 関数が正しくインプリメントされていない”に、定数関数 (Constant Function) のコードも書いてありました。
  1. 2013年06月25日 08:30 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:6

VerilogでXSTにBlock RAMを推論させる

以前にVHDLでBlock RAMを推論させて、外部ファイルの初期化データをロードする方法をやってみたが(”VHDLでのブロックRAMや分散RAMの初期化(16進数で書かれた外部データファイル)”)、今回はVerilogで同様のことをやってみた。

XSTユーザーガイド(Virtex-4、Virtex-5、Spartan-3 および CPLD デバイス用) UG627 (v 12.4) 2010年 12月14日の254ページのv_rams_20cを参考に、バス幅などを変更した。使用したISEは13.1。FPGAはVirtex-5 LX110T-1。ソースコードを下に示す。

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, we,  addr,  din,  dout);
    input  clk;
    input  we;
    input  [4:0]    addr;
    input  [15:0]    din;
    output  [15:0]    dout;
    reg  [15:0]  ram  [0:31];
    reg  [15:0]  dout;
    
    initial begin
        $readmemh("rams_20c.data",ram,  0,  31);
    end
    
    always  @(posedge  clk) begin
        if  (we)
            ram[addr]  <=  din;
            
        dout  <=  ram[addr];
    end
endmodule


rams_20c.dataを下に示す。

AABB
CCDD
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000


これでインプリメントして、FPGA Editorで見てみた。結果は、Block RAMがインスタンスされずに、SLICEMで実装された。
Verilog_BRAM_1_110621.png

これで、困っていたところ、ツイッターで@osamu_takeuchiさんに(* RAM_STYLE="BLOCK" *)を教えていただいた。

reg [15:0] ram [0:31];



(* RAM_STYLE="BLOCK" *) reg [15:0] ram [0:31];


に変更して、もう一度インプリメントしてみた。FPGA Editorで見たところ、Block RAMがインスタンスされていた。
Verilog_BRAM_2_110621.png

初期値も代入されていた。
Verilog_BRAM_3_110621.png

VHDLでは、デフォルトでBlock RAMが推定されたが、Verilogでは、(* RAM_STYLE="BLOCK" *)制約を追加する必要があった。

ちなみに、下のようにバイトイネーブルをつけたところ、(これが本命だったのですが。。。)

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, we,  addr,  din,  dout);
    input  clk;
    input  [1:0]    we;
    input  [4:0]    addr;
    input  [15:0]    din;
    output  [15:0]    dout;
    (* RAM_STYLE="BLOCK" *) reg  [15:0]  ram  [0:31];
    // reg  [15:0]  ram  [0:31];
    reg  [15:0]  dout;
    
    initial begin
        $readmemh("rams_20c.data",ram,  0,  31);
    end
    
    always  @(posedge  clk) begin
        dout  <=  ram[addr];
            
         if  (we[0])
             ram[addr][7:0]  <=  din[7:0];
            
         if  (we[1])
             ram[addr][15:8]  <=  din[15:8];
    end
endmodule


SLICELなどのロジックに推論されてしまった。
Verilog_BRAM_4_110621.png

(2011/06/22:追記)
ツイッターで@osamu_takeuchiさんに教えていただいたが、Project Navigatorのテンプレートにバイト・イネーブルを使用したRAMの例が載っているそうだ。たびたびありがとうございます。
EditメニューのLanguage Templates...を選ぶと出てくるLanguage TemplatesのVerilog -> Synthesis Constructs -> Coding Examples -> RAM -> Block RAM -> Single Port -> Byte-wide Write Enable -> No Change Mode w/ 2bit write enable(for device familes older than Virtex-6 and Spartan-6)を参考にして、v_rams_20c.v を書き換えてみた。
Verilog_BRAM_5_110622.png

v_rams_20c.v のVerilogソースを下に示す。

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, we,  addr,  din,  dout);
   parameter DATA_WIDTH = 16;
   parameter ADDR_WIDTH = 5;

   input    wire [DATA_WIDTH-1:0] din;
   input    wire [ADDR_WIDTH-1:0] addr;
   input    wire [1:0] we;
   input    wire clk;
   output    reg [DATA_WIDTH-1:0] dout;

   wire ram_ena;

   assign ram_ena = 1'b1;
   
   (* RAM_STYLE="BLOCK" *) reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];
   reg [(DATA_WIDTH/2)-1:0] di0, di1;

   //  The following code is only necessary if you wish to initialize the RAM 
   //  contents via an external file (use $readmemb for binary data)
   initial
      $readmemh("rams_20c.data", ram, 0, 31);

   always @(we, din) begin
      if (we[0])
         di0 = din[(DATA_WIDTH/2)-1:0];
      else
         di0 = ram[addr][(DATA_WIDTH/2)-1:0];
      if (we[1])
         di1 = din[DATA_WIDTH-1:DATA_WIDTH/2];
      else
         di1 = ram[addr][DATA_WIDTH-1:DATA_WIDTH/2];
   end

   always @(posedge clk)
      if (ram_ena) begin
         dout  <= {di1,di0};
         ram[addr] <= {di1,di0};
      end
endmodule


インプリメントしたところ、Block RAMに割り当てられていた。
Verilog_BRAM_6_110622.png

Block RAMを拡大してみたところ、WEAL0~3にwe_0_IBUF, we_1_IBUFが割り当てられていた。INT_01_Lには初期化データが入っていることが確認できた。
Verilog_BRAM_7_110622.png

(もう一度追記:重要)
バイト・イネーブル付きRAMですが、Spartan-3はBlock RAMを使用しないでSLICEMで実装されました。下にBlock RAMで実装されたか、SLICEMで実装されたか?を表にします。

Spartan-3 SLICEM
Spartan-3E SLICEM
Spartan-3A Block RAM
Virtex-4 Block RAM
Virtex-5 Block RAM
Spartan-6 Block RAM
Virtex-6 Block RAM


確か、Spartan-3A辺りからBlock RAMにバイト・イネーブルが付いたんじゃなかったかな?
  1. 2011年06月21日 15:34 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:2

Verilogでincludeを使用せずにパラメータをポート宣言に使う

なつたんさん”Verilog のポート宣言にパラメータを使う”で、Verilogのポート宣言にパラメータを遣うときにinclude文でparameter値をインクルードしないで、モジュールの後に直接parameterを記述する記述方法の紹介があった。(下に引用させていただきます)

module add   #(parameter width = 8)


上のモジュールから使用するときはparameter文に値を入れるのと同様に書けばよいとのこと。(またまた下に引用させていただきます)

add #(.width(4) ) add_u(.clk(clk), .a(a), .b(b), .c(c));


知らなかった。
今まで、私はインクルード用のファイルにparameter文を書いて、それをmodule宣言の後でインクルードしていた。こんな感じ。

module ddr2_sdram_cont (clk_in, clk_out, reset, input_data, input_mask...);
    `include "./ddr2_cont_parameters.vh"
    
    input clk_in; // クロック入力
    output clk_out; // クロック出力
    input reset;
    input [INTERFACE_DATA_WIDTH-1 : 0] input_data;
    input [INTERFACE_MASK_WIDTH-1 : 0] input_mask;


インクルードされたddr2_cont_parameter.vhにはparameter文が書いてある。

parameter DDR2_DATA_WIDTH = 16; // DDR2 SDRAMのデータ幅
parameter INTERFACE_DATA_WIDTH = DDR2_DATA_WIDTH*2; // DDR2 SDRAMは4バースト、2倍データ幅で2回書き込む
parameter INTERFACE_MASK_WIDTH = INTERFACE_DATA_WIDTH/8; // BEの幅


今まではこのインクルード方式しかないと思っていた。
共通に使いまわす値でなければ、これからはなつたんさんが紹介された書き方で書こうと思う。
なつたんさんありがとうございました。勉強になりました。
  1. 2008年09月12日 05:32 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:2
»