FC2カウンター FPGAの部屋 OVL(Open Verification Library)を試してみる6(ovl_next, ovl_frame その2)
fc2ブログ

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

FPGAの部屋

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

OVL(Open Verification Library)を試してみる6(ovl_next, ovl_frame その2)

OVL(Open Verification Library)を試してみる5(ovl_next, ovl_frame その1)”の続き。

さて、request とacknowledge の関係をVerilogでコーディングしてみた。req0, ta0, ack0 とreq1, ta1, ack1 の信号のグループがある。req_start0の1パルスをreq0を生成するステートマシン(req_sm.v)が受けると、req0信号をアサートする。req0は応答が帰ってくるまでアサートし続ける。ack0を出力するステートマシン(ack_sm.v)は、バックエンドの回路からのta0のアサートを確認して、ack0をアサートする。ack0がアサートされると、req0はディアサートされる、といったような制御をする。タイミングチャートを下に示す。
OVL_req_ack_1_100318.png

req_start1, req1, ta1, ack1の関係もreq0などの関係と同様とする。今回は間違いやすくするために、ack_sm.v はreq0,req1を同時に受けて、ack0, ack1を返すステートマシンとする(ta0, ta1も2つ同時に受ける)。それにわざと間違いを入れておいた(思いもよらない間違いも入っていたが。。。)。実際に作るときは、ack0, ack1のステートマシンを分けて、2つのステートマシンで処理する。しかし、仕様で同様の状態になってしまうケースもあることから、OVLを使用したアサーションの例として作成した。

req_sm.v を下に示す。

// OVLテスト用req_sm.v
// req_start 信号を受けると、reqを1にアサートする。ackを受け取るとreqを0にディアサートする

`default_nettype none

module req_sm (
    input wire    clk,
    input wire    reset,
    input wire    req_start,
    input wire    ack,
    output reg    req
);
    
    parameter [1:0]    idle_req        = 2'b01,
                    assert_req    = 2'b10;
    
    reg [1:0] cs_req;
    
    always @(posedge clk) begin
        if (reset) begin
            cs_req <= idle_req;
            req <= 1'b0;
        end else begin
            case (cs_req)
                idle_req : 
                    if (req_start) begin
                        cs_req <= assert_req;
                        req <= 1'b1;
                    end
                assert_req :
                    if (ack) begin
                        cs_req <= idle_req;
                        req <= 1'b0;
                    end
            endcase
        end
    end
endmodule

`default_nettype wire


次に、ack_sm.v を示す。

// OVLテスト用 ack_sm.v
// 2つのreqを管理して、reqがアサートされている状態で、taがアサートされるとackを返す

`default_nettype none

module ack_sm (
    input wire    clk,
    input wire    reset,
    input wire    req0,
    input wire    req1,
    input wire    ta0, // Transfer Acknowledge
    input wire    ta1, // Transfer Acknowledge 
    output reg    ack0,
    output reg    ack1
);
    
    parameter [6:0]    idle_req         = 7'b0000001,    // 最初のidle 状態
                    assert_req0        = 7'b0000010,    // req0だけがアサートされている
                    assert_req1        = 7'b0000100,    // req1だけがアサートされている
                    assert_req01    = 7'b0001000,
                    req0_ack        = 7'b0010000,
                    req1_ack        = 7'b0100000,
                    req01_ack        = 7'b1000000;
    reg [6:0] cs_main;
    
    always @(posedge clk) begin
        if (reset) begin
            cs_main <= idle_req;
            ack0 <= 1'b0; ack1 <= 1'b0;
        end else begin
            case (cs_main)
                idle_req : begin
                    if (req0) begin
                        cs_main <= assert_req0;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end else if (req1) begin
                        cs_main <= assert_req1;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end
                end
                assert_req0 : begin
                    if (req1) begin
                        cs_main <= assert_req01;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end else if (ta0) begin
                        cs_main <= req0_ack;
                        ack0 <= 1'b1; ack1 <= 1'b0;
                    end
                end
                assert_req1 : begin
                    if (req0) begin
                        cs_main <= assert_req01;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end else if (ta1) begin
                        cs_main <= req1_ack;
                        ack0 <= 1'b0; ack1 <= 1'b1;
                    end
                end
                assert_req01 : begin
                    if (ta0 & ~ta1) begin
                        cs_main <= req0_ack;
                        ack0 <= 1'b1; ack1 <= 1'b0;
                    end else if (~ta0 & ta1) begin
                        cs_main <= req1_ack;
                        ack0 <= 1'b0; ack1 <= 1'b1;
                    end else if (ta0 & ta1) begin
                        cs_main <= req01_ack;
                        ack0 <= 1'b1; ack1 <= 1'b1;
                    end
                end
                req0_ack : begin
                    if (req1) begin
                        cs_main <= assert_req1;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end else begin
                        cs_main <= idle_req;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end
                end
                req1_ack : begin
                    if (req0) begin
                        cs_main <= assert_req0;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end else begin
                        cs_main <= idle_req;
                        ack0 <= 1'b0; ack1 <= 1'b0;
                    end
                end
                req01_ack : begin
                    cs_main <= idle_req;
                    ack0 <= 1'b0; ack1 <= 1'b0;
                end
            endcase
        end
    end
endmodule

`default_nettype wire


わざと間違いを入れてある。しかし、ack0, ack1を別々のステートマシンでやった方が、相当簡単になる。2つ一緒にやるのは難しい。
次は、OVLの入ったテストベンチを示す。req_sm.v は2つインスタンスを作っている。ack_sm.v は1つインスタンスしている。

// tb_req_ack.v

`default_nettype none
`timescale 1ns / 10ps

`include "std_ovl_defines.h"

module tb_req_ack;
    reg clk;
    reg reset;
    reg req_start0, req_start1;
    wire req0, req1;
    wire ack0, ack1;
    reg ta0, ta1;
    wire [`OVL_FIRE_WIDTH-1:0] fire_ta0_ack0_as, fire_ta1_ack1_as;
    wire [`OVL_FIRE_WIDTH-1:0] fire_req0_ack0_as, fire_req1_ack1_as;

    parameter CLK_PERIOD = 20;
    
    // 50MHzクロック
    always begin
       #(CLK_PERIOD/2)    clk = 1'b1 ;
       #(CLK_PERIOD/2)    clk = 1'b0 ;
    end
    
    // reset
    initial begin
            reset = 1'b1;
        #100    reset = 1'b0;
    end
    
    req_sm req_sm0 (
        .clk(clk),
        .reset(reset),
        .req_start(req_start0),
        .ack(ack0),
        .req(req0)
    );
    req_sm req_sm1 (
        .clk(clk),
        .reset(reset),
        .req_start(req_start1),
        .ack(ack1),
        .req(req1)
    );
    
    ack_sm ack_sm_i (
        .clk(clk),
        .reset(reset),
        .req0(req0),
        .req1(req1),
        .ta0(ta0),
        .ta1(ta1),
        .ack0(ack0),
        .ack1(ack1)
    );
    
    // req_start0とta0の関係を生成する
    initial begin
        req_start0 <= 1'b0;
        ta0 <= 1'b0;
        @(negedge reset); // リセットが0になるのを待つ
        #100;
        REQ_START_TA0(3'd1);
        REQ_START_TA0(3'd2);
        REQ_START_TA0(3'd3);
        REQ_START_TA0(3'd2);
        REQ_START_TA0(3'd1);
        REQ_START_TA0(3'd3);
        REQ_START_TA0(3'd2);
        #200    $stop;
    end
    
    // req_start1とta1の関係を生成する
    initial begin
        req_start1 <= 1'b0;
        ta1 <= 1'b0;
        @(negedge reset); // リセットが0になるのを待つ
        #100;
        REQ_START_TA1(3'd2);
        REQ_START_TA1(3'd1);
        REQ_START_TA1(3'd2);
        REQ_START_TA1(3'd3);
        REQ_START_TA1(3'd3);
        REQ_START_TA1(3'd1);
        REQ_START_TA1(3'd2);
    end
    
    // task REQ_START_TA0
    task REQ_START_TA0;
        input [2:0] loop_count;
        

        begin :REQ_START_TA0_PROCESS
            integer i;            
            
            @(posedge clk); // clkの立ち上がりまでwait
            #1 ; // 遅延を挟んで
            req_start0 = 1'b1;
            @(posedge clk); // clkの立ち上がりまでwait
            #1; // 遅延を挟んで
            req_start0 = 1'b0;
            
            for(i=loop_count; i>0; i=i-1) begin
                @(posedge clk);
                #1;
            end
            ta0 = 1'b1;
            @(posedge clk); // clkの立ち上がりまでwait
            #1 ; // 遅延を挟んで
            ta0 = 1'b0;
        end
    endtask
    
    // task REQ_START_TA1
    task REQ_START_TA1;
        input [2:0] loop_count;
        

        begin :REQ_START_TA1_PROCESS
            integer i;        
            
            @(posedge clk); // clkの立ち上がりまでwait
            #1 ; // 遅延を挟んで
            req_start1 = 1'b1;
            @(posedge clk); // clkの立ち上がりまでwait
            #1; // 遅延を挟んで
            req_start1 = 1'b0;
            
            for(i=loop_count; i>0; i=i-1) begin
                @(posedge clk);
                #1;
            end
            ta1 = 1'b1;
            @(posedge clk); // clkの立ち上がりまでwait
            #1 ; // 遅延を挟んで
            ta1 = 1'b0;
        end
    endtask
    
    // アサーション
    // ta0がアサートされた次のクロックでack0がアサートされる
    ovl_next #(
        `OVL_ERROR,            // severity_level
        1,                    // num_cks(1クロック後にアサート)
        1,                    // check_overlapping (off)
        1,                    // check_missing_start (on)
        `OVL_ASSERT,        // property_type
        // "ERROR: ta0がアサートされた次のクロックでack0がアサートされていない",
        "ERROR: ack0 isn't asserted of with the next clock that ta0 was asserted.",
        `OVL_COVER_BASIC,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_HIGH,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) ta0_ack0_assertion (
        clk,
        reset,
        1'b1,                // enable
        ta0,                // start event
        ack0,                // test_expr
        fire_ta0_ack0_as    // fire
    );
    
    // ta1がアサートされた次のクロックでack1がアサートされる
    ovl_next #(
        `OVL_ERROR,            // severity_level
        1,                    // num_cks(1クロック後にアサート)
        1,                    // check_overlapping (off)
        1,                    // check_missing_start (on)
        `OVL_ASSERT,        // property_type
        // "ERROR: ta1がアサートされた次のクロックでack1がアサートされていない",
        "ERROR: ack1 isn't asserted of with the next clock that ta1 was asserted.",
        `OVL_COVER_BASIC,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_HIGH,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) ta1_ack1_assertion (
        clk,
        reset,
        1'b1,                // enable
        ta1,                // start event
        ack1,                // test_expr
        fire_ta1_ack1_as    // fire
    );
    
    // req0アサートされた後で、ack0 が2~4クロックの間に1になる
    ovl_frame #(
        `OVL_ERROR,                // severity_level
        2,                        // min_cks
        4,                        // max_cks
        `OVL_IGNORE_NEW_START,    // action_on_new_start
        `OVL_ASSERT,            // property_type
        // "ERROR: req0がアサートされた後の2~4クロック後にack0が1にならない",
        "ERROR: ack0 isn't asserted after 2-4 clocks after req0 was asserted.",
        `OVL_COVER_BASIC,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) req0_ack0_assertion (
        clk,
        reset,
        1'b1,                // enable
        req0,                // start event
        ack0,                // test_expr
        fire_req0_ack0_as    // fire
    );
        
    // req1アサートされた後で、ack1 が2~4クロックの間に1になる
    ovl_frame #(
        `OVL_ERROR,                // severity_level
        2,                        // min_cks
        4,                        // max_cks
        `OVL_IGNORE_NEW_START,    // action_on_new_start
        `OVL_ASSERT,            // property_type
        // "ERROR: req1がアサートされた後の2~4クロック後にack1が1にならない",
        "ERROR: ack1 isn't asserted after 2-4 clocks after req1 was asserted.",
        `OVL_COVER_BASIC,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) req1_ack1_assertion (
        clk,
        reset,
        1'b1,                // enable
        req1,                // start event
        ack1,                // test_expr
        fire_req1_ack1_as    // fire
    );
        
endmodule

`default_nettype wire


Veritakの場合には日本語のメッセージを活かしてある。
さて、Veritakを起動して、プロジェクトを作成してシミュレーションしてみた。
下にVeritakのプロジェクトの様子を示す。define値を設定している。今回はOVL_V1のassert_... を使用してないので、.vlibは使用しない。
OVL_req_ack_2_100318.png

次にシミュレーション結果を下の図に示す。
OVL_req_ack_3_100318.png

黒い縦線のところでack0 がアサートされていないのがわかる。これに対応するovl_nextのエラーメッセージを下に示す。

OVL_ERROR : OVL_NEXT : ERROR: ta0がアサートされた次のクロックでack0がアサートされていない : Test expression is not asserted after elapse of num_cks cycles from start event : severity 1 : time 29000 : tb_req_ack.ta0_ack0_assertion.ovl_error_t


これは、fire信号、[2:0]fire_ta0_ack0_as でも確認することができる。値の4はカバーなので、1がアサーション・エラーを示す値となる。
次に、ovl_frame の方だが、330nsec の時点で、アサーション・エラーとなっている。メッセージを下に示す。

OVL_ERROR : OVL_FRAME : ERROR: req0がアサートされた後の2~4クロック後にack0が1にならない : Test expression is not TRUE within specified maximum max_cks cycles from start event : severity 1 : time 33000 : tb_req_ack.req0_ack0_assertion.ovl_error_t


こちらのfire信号、fire_req0_ack0_asは0のままだった。ドライバ記述部に飛んで、記述を見てみたが、すべて0となっていた。ovl_frame のfire はOVLのバージョン2.4ではサポートされていないようだ。

ovl_next とovl_frame アサーションを使用して、波形の自動チェックができることがわかった。エラーが出たらアサーションが止まるようにしておくと良いかもしれない(severity_levelにOVL_ERRORのかわりにOVL_FATALを記述する)。

最後に、ModelSimでもやってみたので、図を下に示す。
OVL_req_ack_4_100318.png
  1. 2010年03月18日 05:20 |
  2. アサーション事始め
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/1409-5c056070
この記事にトラックバックする(FC2ブログユーザー)