SSブログ

部品:AXIストリームレジスタ シミュレーション [AXI]

axis_sliceをシミュレーションしてみます。

テストパターン発生に先のaxis_source,axis_sinkを使います。で、早速変更してしまってます。Xilinxのisim(無償版)を試してみたのですが、「input wire integer ratio」の構文が通らなかったので、ratioを内部変数にしてます。そういえば元々この構文変だなぁと思いながらもModelSim(Altera版)では通っちゃってたので、まぁいっかで放ってた物でした。
「実体名.ratio<=値」の形で変更します。

axis_source、ソース側
module  axis_source
(
    input   wire  enable,   // enable
    input   wire  c_clk,    // Clock
    input   wire  c_rst,    // Reset
    input   wire  m_ready,  // Ready
    output  wire  m_valid   // Valid
);
    integer ratio;  // ratio

    reg   [3:0]  rand;
    reg   inhib;
    reg   valid;

    assign  m_valid = valid & ~inhib;

    always @(posedge c_clk) begin
        if (c_rst) begin
            valid   <= 1'b0;
            rand    <= 0;
            inhib   <= 1'b1;
        end
        else begin
            valid    <= enable;
            if (inhib | m_ready) begin
                rand    = $random;
                case (ratio)
                    default  : inhib    <= 0;
                    1        : inhib    <= &rand[3:0];
                    2        : inhib    <= &rand[2:0];
                    3        : inhib    <= &rand[1:0];
                    4        : inhib    <=  rand[0];
                    5        : inhib    <= |rand[1:0];
                    6        : inhib    <= |rand[2:0];
                    7        : inhib    <= |rand[3:0];
                endcase
            end
        end
    end
endmodule

axis_sink、シンク側
module    axis_sink
(
    input   wire  enable,   // enable
    input   wire  c_clk,    // Clock
    input   wire  c_rst,    // Reset
    output  wire  s_ready,  // Ready
    input   wire  s_valid   // Valid
);
    integer ratio;  // ratio

    reg   [3:0]  rand;
    reg   inhib;
    reg   ready;

    assign  s_ready = ready & ~inhib;

    always @(posedge c_clk) begin
        if (c_rst) begin
            ready    <= 1'b0;
            rand     <= 0;
            inhib    <= 1'b0;
        end
        else begin
            ready    <= enable;
            rand     = $random;
            case (ratio)
                default  : inhib    <= 0;
                1        : inhib    <= &rand[3:0];
                2        : inhib    <= &rand[2:0];
                3        : inhib    <= &rand[1:0];
                4        : inhib    <=  rand[0];
                5        : inhib    <= |rand[1:0];
                6        : inhib    <= |rand[2:0];
                7        : inhib    <= |rand[3:0];
            endcase
        end
    end
endmodule


axis_sliceテスト
`timescale 1ns / 1ps
`default_nettype none

module    tb;
    parameter  stdw = 16;

    reg     c_clk;
    reg     c_rst;

    reg     enable;

    initial     c_clk <= 1'b0;
    always  #5  c_clk <= ~c_clk;

    initial begin
        c_rst       <= 1'b1;
        enable      <= 1'b0;
        #100;

        @(posedge c_clk);
        c_rst       <= 1'b0;
        #200;

        @(posedge c_clk);
        enable      <= 1'b0;
        @(posedge c_clk);
        src.ratio   <= 0;
        snk.ratio   <= 0;
        @(posedge c_clk);
        enable      <= 1'b1;
        #1000;

        @(posedge c_clk);
        enable      <= 1'b0;
        @(posedge c_clk);
        src.ratio   <= 5;
        snk.ratio   <= 3;
        @(posedge c_clk);
        enable      <= 1'b1;
        #2000;

        @(posedge c_clk);
        enable      <= 1'b0;
        @(posedge c_clk);
        src.ratio   <= 3;
        snk.ratio   <= 5;
        @(posedge c_clk);
        enable      <= 1'b1;
        #2000;

        $display("finish");
        $stop;
    end

    wire              ts1_ready;
    wire              ts1_valid;
    wire  [stdw-1:0]  ts1_data ;
    wire              ts2_ready;
    wire              ts2_valid;
    wire  [stdw-1:0]  ts2_data ;
    wire              ts3_ready;
    wire              ts3_valid;
    wire  [stdw-1:0]  ts3_data ;

    ///////////////////////////////
    axis_source
    src
    (
        .enable    (enable     ),
        .c_clk     (c_clk      ),
        .c_rst     (c_rst      ),
        .m_ready   (ts1_ready  ),
        .m_valid   (ts1_valid  ) 
    );

    reg     [stdw-1:0]    s_data;
    always @(posedge c_clk) begin
        if      (c_rst)                  s_data <= 0;
        else if (ts1_ready & ts1_valid)  s_data <= s_data + 1;
    end

    assign  ts1_data = s_data;


    ///////////////////////////////
    axis_sink
    snk
    (
        .enable    (enable     ),
        .c_clk     (c_clk      ),
        .c_rst     (c_rst      ),
        .s_ready   (ts3_ready  ),
        .s_valid   (ts3_valid  ) 
    );

    reg     [stdw-1:0]    d_data;
    always @(posedge c_clk) begin
        if      (c_rst)                  d_data <= 0;
        else if (ts3_ready & ts3_valid)  d_data <= d_data + 1;
    end

    always @(posedge c_clk) begin
        if (ts3_ready & ts3_valid) begin
            if (ts3_data != d_data) begin
                $display("error %x,%x", ts3_data, d_data);
                $stop;
            end
        end
    end


    ///////////////////////////////
    axis_slice    #(stdw)
    ts1_to_ts2 (
        c_clk, c_rst,                      // clk, rst
        ts1_ready, ts1_valid, ts1_data,    // sink
        ts2_ready, ts2_valid, ts2_data     // source
    );

    axis_slice    #(stdw)
    ts2_to_ts3 (
        c_clk, c_rst,                      // clk, rst
        ts2_ready, ts2_valid, ts2_data,    // sink
        ts3_ready, ts3_valid, ts3_data     // source
    );

endmodule

`default_nettype wire

[src] --ts1--> [ts1_to_ts2] --ts2--> [ts2_to_ts3] --ts3--> [snk]
と数珠つなぎになってます。ソース側で発生させるデータ列はカウント値、シンク側でも同じように発生させて、同じ順番になってなければエラーです。
※'X'や'Z'は見てないので、テストとしては不十分ですが…
最初はソース、シンクとも100%。途中でソースの帯域<シンクの帯域、ソースの帯域>シンクの帯域、と変化させています。

isim起動のバッチファイルとプロジェクトファイルの例。コマンドラインから「isim.bat」で実行です。
tb.prj、プロジェクト
verilog  work  ../axis_slice.v
verilog  work  ../axis_txgen.v
verilog  work  tb.v
isim.bat、実行バッチ
fuse work.tb -prj tb.prj -o tb.exe
tb.exe -gui -wdb tb.wdb

結果。まずは全体。最後まで行ってるので途中データが化けるようなことは起きて無いようです。
axis_slice_sim1.png
切り替わりのあたり。波形を見てみると、validは上流→下流に、readyは下流→上流に伝搬してるのがわかります。思った通りの動作、のはず。
axis_slice_sim0.png

ちなみにこのモジュール、「深さ1の小さなFIFO+出力レジスタ」です。後でFIFOも作りますが、「深さ1」と「深さ2以上」に大きな壁があるので分けて記述しました。
色々使えます。何度か紹介してる「周波数を出すために経路を分ける」という使い方や、非同期のループを回避するのにも使います。例えばreadyとvalidが組み合わせ回路のモジュール同士をつなぐとこうなる可能性があります。
async_loop.png
この場合、間にaxis_sliceを入れて解決します。毎回注意するのも面倒なので、非同期のモジュールはaxis_sliceでサンドイッチするようにしてます。逆に言うと気にすることなく組み合わせ回路で作れます。マルチプレクサなどは組み合わせ回路で記述してしまった方が楽だったりします。

「AXIにはデータだけじゃなくIDやLastとかあるよ?どうするの?」という話。
AXIは"ready"と"valid"と"それ以外の荷物"という関係です。なので、こう書けます。
axis_slice    #(IDの幅+データの幅+…)
sync (
    c_clk, c_rst,                                // clk, rst
    s_ready, s_valid, {s_id, s_last, s_data},    // sink
    m_ready, m_valid, {m_id, m_last, m_data}     // source
);
ここはverilog記述が楽ですね~VHDLやSystemVerilogだったら構造体使うかな?

注意点としてはレイテンシは増えることです。多くの場合周波数は上げられるので、スループットは上がります。どちら重視かはシステム次第です。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

Facebook コメント

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。