FC2カウンター FPGAの部屋 AX4 Lite Slave IPの作製
fc2ブログ

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

FPGAの部屋

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

AXI4 Lite Master BFM の Verilog HDL コード

AXI4 Lite Master BFM の Verilog HDL コードを貼っておく。AXI4 Lite Master BFM は、AXI4 Master BFM のラッパーとして作られている。
Verilog HDL の task で書いてあって、下に示す。7つの task がある。

AXI_LiteM_1Seq_Write(Write Address, Write Data, Write Response をシーケンシャルにオーバーラップせずに行う)
AXI_LiteM_WAC(Write Address Channel の Transaction を実行する)
AXI_LiteM_WDC(Write Data Channel の Transaction を実行する)
AXI_LiteM_WRC(Write Response Channel の Transaction を実行する)
AXI_LiteM_1Seq_Read(Read Address, Read Data をシーケンシャルに行う)
AXI_LiteM_RAC(Read Address Channel の Transaction を実行する)
AXI_LiteM_RDC(Read Data Channel の Transaction を実行する)


下に、Verilog HDL コードを示す。

// AXI4 bus Lite Master Bus Fucntion Mode
// 2013/12/14
// AXI4_Master_BFM のラッパー
//
//

`default_nettype none

`timescale 100ps / 1ps

module AXI4_Lite_Master_BFM #(
    parameter DELAY = 10 )
(
    input   wire    ACLK,

    output wire [31:0] S_AXI_AWADDR,
    output wire [2:0] S_AXI_AWPROT,
    output wire S_AXI_AWVALID,
    output wire [31:0] S_AXI_WDATA,
    output wire [3:0] S_AXI_WSTRB,
    output wire S_AXI_WVALID,
    output wire S_AXI_BREADY,
    output wire [31:0] S_AXI_ARADDR,
    output wire [2:0] S_AXI_ARPROT,
    output wire S_AXI_ARVALID,
    output wire S_AXI_RREADY,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [1:0] S_AXI_BRESP,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RVALID
);

    parameter   ASIZE_BT_4  = 3'd2; // 32 bit width
    parameter   ASIZE_BT_2 = 3'd1;  // 16 bit width
    parameter   ASIZE_BT_1 = 3'd0;  // 8 bit width

    parameter   ABURST_FIXED    = 2'd0;
    parameter   ABURST_INCR = 2'd1;
    parameter   ABURST_WRAP = 2'd2;

    // RESP の値の定義
    parameter   RESP_OKAY =     2'b00;
    parameter   RESP_EXOKAY =   2'b01;
    parameter   RESP_SLVERR =   2'b10;
    parameter   RESP_DECERR =   2'b11;

    reg  [7:0]  awlen_hold = 0;
    reg  [0:0]  wid_hold = 0;
    reg  axi_w_transaction_active = 0;
    reg  axi_r_transaction_active = 0;
    reg  [7:0]  arlen_hold = 0;

    // AXI4_BFM のインスタンス
    AXI4_Master_BFM #(.DELAY(DELAY)) MBFMi(
        .ACLK(ACLK),
        .S_AXI_AWID(),
        .S_AXI_AWADDR(S_AXI_AWADDR),
        .S_AXI_AWLEN(),
        .S_AXI_AWSIZE(),
        .S_AXI_AWBURST(),
        .S_AXI_AWLOCK(),
        .S_AXI_AWCACHE(),
        .S_AXI_AWPROT(S_AXI_AWPROT),
        .S_AXI_AWREGION(),
        .S_AXI_AWQOS(),
        .S_AXI_AWUSER(),
        .S_AXI_AWVALID(S_AXI_AWVALID),
        .S_AXI_AWREADY(S_AXI_AWREADY),
        .S_AXI_WID(),
        .S_AXI_WDATA(S_AXI_WDATA),
        .S_AXI_WSTRB(S_AXI_WSTRB),
        .S_AXI_WLAST(),
        .S_AXI_WUSER(),
        .S_AXI_WVALID(S_AXI_WVALID),
        .S_AXI_WREADY(S_AXI_WREADY),
        .S_AXI_BID(1'b0),
        .S_AXI_BRESP(S_AXI_BRESP),
        .S_AXI_BUSER(1'b0),
        .S_AXI_BVALID(S_AXI_BVALID),
        .S_AXI_BREADY(S_AXI_BREADY),
        .S_AXI_ARID(),
        .S_AXI_ARADDR(S_AXI_ARADDR),
        .S_AXI_ARLEN(),
        .S_AXI_ARSIZE(),
        .S_AXI_ARBURST(),
        .S_AXI_ARLOCK(),
        .S_AXI_ARCACHE(),
        .S_AXI_ARPROT(S_AXI_ARPROT),
        .S_AXI_ARREGION(),
        .S_AXI_ARQOS(),
        .S_AXI_ARUSER(),
        .S_AXI_ARVALID(S_AXI_ARVALID),
        .S_AXI_ARREADY(S_AXI_ARREADY),
        .S_AXI_RID(1'b0),
        .S_AXI_RDATA(S_AXI_RDATA),
        .S_AXI_RRESP(S_AXI_RRESP),
        .S_AXI_RLAST(1'b1),
        .S_AXI_RUSER(1'b0),
        .S_AXI_RVALID(S_AXI_RVALID),
        .S_AXI_RREADY(S_AXI_RREADY)
    );

    // Write Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_LiteM_1Seq_Write; // Write Address, Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input   [31:0]  awaddr;
        input   [31:0]  wdata;
        input   [7:0]   wait_clk_bready;
        input   [7:0]   wmax_wait;
        begin
            MBFMi.AXI_Master_1Seq_Write(1'b0, awaddr, 8'd0, ASIZE_BT_4, ABURST_INCR, wdata, wait_clk_bready, wmax_wait);
        end
    endtask

    // Write Address Channel
    task AXI_LiteM_WAC;
        input   [31:0]  awaddr;
        begin
            MBFMi.AXI_MASTER_WAC(1'b0, awaddr, 8'd0, ASIZE_BT_4, ABURST_INCR);
        end
    endtask

    // Write Data Channel
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_LiteM_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input   [31:0]  wdata;
        input   [7:0]   wmax_wait;  // Write時の最大wait数
        begin
            MBFMi.AXI_MASTER_WDC(wdata, wmax_wait);
        end
    endtask

    // Write Response Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    task AXI_LiteM_WRC;    // wait_clk_bready
        input   [7:0]   wait_clk_bready;
        begin
            MBFMi.AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask

    // Read Channel
    task AXI_LiteM_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input   [31:0]  araddr;
        input   [7:0]   rmax_wait;  // Read時の最大wait数
        begin
            MBFMi.AXI_Master_1Seq_Read(1'b0, araddr, 8'd0, ASIZE_BT_4, ABURST_INCR);
        end
    endtask

    // Read Address Channel
    task AXI_LiteM_RAC;
        input   [31:0]  araddr;
        begin
            MBFMi.AXI_MASTER_RAC(1'b0, araddr, 8'd0, ASIZE_BT_4, ABURST_INCR);
        end
    endtask

    // Read Data Channel
    task AXI_LiteM_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        input   [7:0]   rmax_wait;  // Read時の最大wait数
        begin
            MBFMi.AXI_MASTER_RDC(rmax_wait);
        end
    endtask
endmodule

`default_nettype wire

  1. 2013年12月15日 04:07 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.4(OVLチェッカのVerilog コード)

前の記事は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”だが、全体の関連は、”AXI4 Slave IPの作製”を参照のこと。

OVLチェッカはOVL(Open Verification Library)を使用して、AXIバスの信号が規格から外れていないか?をチェックするチェッカだ。OVLだけでなく、Verilogコードも書いてチェックしてあるが、まだまだルールが足りないと思っている。OVLでもどのようにチェッカを使うか迷う場面が少なくない。汎用性を持たせようと思うと、並列のトランザクションに対応する必要がある。それにはまだ対応していないが、OVLチェッカだけでは難しいのではないか?と考えている。例えば、AWVALIDとAWREADYの組みとWVALID, WREADY, WLASTの組みのカウント数が合っているか?をチェックするチェッカが必要なのではないか?と思っている。

現在のOVL_Checker.v を下に貼っておく。

// OVL_Checker.v
// 2012/10/25:書き込みトランザクションの時に、MasterがAWREADYを待って、WVALIDをアサートしていけないというルールがあったので、修正した。
//

`default_nettype none

`timescale 100ps / 1ps

`include "std_ovl_defines.h"

module OVL_Checker (
    input    wire    ACLK,
    input    wire    ARESETN,

    input wire [0:0] S_AXI_AWID,
    input wire [31:0] S_AXI_AWADDR,
    input wire [7:0] S_AXI_AWLEN,
    input wire [2:0] S_AXI_AWSIZE,
    input wire [1:0] S_AXI_AWBURST,
    input wire [1:0] S_AXI_AWLOCK,
    input wire [3:0] S_AXI_AWCACHE,    // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_AWPROT,
    input wire [3:0] S_AXI_AWREGION,
    input wire [3:0] S_AXI_AWQOS,
    input wire [0:0] S_AXI_AWUSER,
    input wire S_AXI_AWVALID,
    input wire [0:0] S_AXI_WID,
    input wire [31:0] S_AXI_WDATA,
    input wire [3:0] S_AXI_WSTRB,
    input wire S_AXI_WLAST,
    input wire [0:0] S_AXI_WUSER,
    input wire S_AXI_WVALID,
    input wire S_AXI_BREADY,
    input wire [0:0] S_AXI_ARID,
    input wire [31:0] S_AXI_ARADDR,
    input wire [7:0] S_AXI_ARLEN,
    input wire [2:0] S_AXI_ARSIZE,
    input wire [1:0] S_AXI_ARBURST,
    input wire [1:0] S_AXI_ARLOCK,
    input wire [3:0] S_AXI_ARCACHE, // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_ARPROT,
    input wire [3:0] S_AXI_ARREGION,
    input wire [3:0] S_AXI_ARQOS,
    input wire [0:0] S_AXI_ARUSER,
    input wire S_AXI_ARVALID,
    input wire S_AXI_RREADY,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    wire [`OVL_FIRE_WIDTH-1:0] fire_wr_data, fire_rd_data;
    wire [`OVL_FIRE_WIDTH-1:0] fire_aw_hcheck, fire_ar_hcheck;
    wire [`OVL_FIRE_WIDTH-1:0] fire_awvalid_cont, fire_wd_sig_assert;
    wire [`OVL_FIRE_WIDTH-1:0] fire_ar_never;
    reg        [7:0]    countw, countr;
    
    parameter    idle_wts =        3'b001,
                wr_data_tran =    3'b010,
                wr_resp_tran =    3'b100;
    reg    [2:0]    wr_tran_cs;
    
    parameter    idle_rts =        1'b0,
                rd_data_tran =    1'b1;
    reg    rd_trans_cs;
    
    
    // Wirte Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Write data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) wr_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_AWVALID & S_AXI_AWREADY),    // reset, Write のアドレス転送でリセット
        S_AXI_WVALID & S_AXI_WREADY,                    // enable
        S_AXI_WDATA,            // test_expr
        fire_wr_data            // fire    parameter
    );
    
    // Read Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Read data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) rd_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_ARVALID & S_AXI_ARREADY),    // reset, Read のアドレス転送でリセット
        S_AXI_RVALID & S_AXI_RREADY,                    // enable
        S_AXI_RDATA,            // test_expr
        fire_rd_data            // fire    parameter
    );
    
    
    // S_AXI_AWVALID とS_AXI_AWREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_AWREADY and S_AXI_AWVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) aw_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // req
        S_AXI_AWREADY,            // ack
        fire_aw_hcheck            // fire parameter
    );
    
    // S_AXI_ARVALID とS_AXI_ARREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_ARREADY and S_AXI_ARVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) ar_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARVALID,            // req
        S_AXI_ARREADY,            // ack
        fire_ar_hcheck            // fire parameter
    );
    
    // Write, S_AXI_AWREADY がアサートされるとき、次のクロックでS_AXI_AWVALIDがディアサートされる
    ovl_transition #(
        `OVL_ERROR,                // severity_level
        1,                        // width
        `OVL_ASSERT,            // property_type
        "Error : Assert of S_AXI_AWVALID",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) awvalid_cont_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // test_expr
        S_AXI_AWREADY & S_AXI_AWVALID,    // start_state
        ~S_AXI_AWVALID,            // next_state
        fire_awvalid_cont
    );
    
    // Write, S_AXI_WVALID, S_AXI_WLAST, S_AXI_WREADYがアサートされるとき、次のクロックで、それらの信号がディアサートされる
    ovl_transition #(
        `OVL_ERROR,                // severity_level
        1,                        // width
        `OVL_ASSERT,            // property_type
        "Error : Assert of S_AXI_WVALID or S_AXI_WLAST or S_AXI_WREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) wd_sig_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // test_expr
        S_AXI_WVALID & S_AXI_WLAST & S_AXI_WREADY,    // start_state
        ~(S_AXI_WVALID | S_AXI_WLAST | S_AXI_WREADY),            // next_state
        fire_wd_sig_assert
    );
    
    // Read, S_AXI_RREADY がアサートされるときは、 S_AXI_RVALID, S_AXI_RREADY, S_AXI_RLAST はアサートされない
    ovl_never #(
        `OVL_ERROR,                // severity_level
        `OVL_ASSERT,            // property_type
        "Read, Assert Error of S_AXI_RREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) ar_never_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST),
        fire_ar_never
    );
    
    // Write の転送数をカウントして、 S_AXI_WLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countw <= 0;
        else begin
            if (S_AXI_AWVALID & S_AXI_AWREADY) begin // countw へロード
                countw <= S_AXI_AWLEN;
            end else if (S_AXI_WVALID & S_AXI_WREADY) begin // データ転送
                if (countw==0) begin // データ転送終了
                    if (~S_AXI_WLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countw==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Read の転送数をカウントして、 S_AXI_RLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countr <= 0;
        else begin
            if (S_AXI_ARVALID & S_AXI_ARREADY) begin // countw へロード
                countr <= S_AXI_ARLEN;
            end else if (S_AXI_RVALID & S_AXI_RREADY) begin // データ転送
                if (countr==0) begin // データ転送終了
                    if (~S_AXI_RLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countr==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Write 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            wr_tran_cs <= idle_wts;
        else begin
            case (wr_tran_cs)
                idle_wts :
                    if (S_AXI_AWREADY & (S_AXI_BREADY)) // エラー
                        $display("%m: at time %t S_AXI_AWREADY がアサートされた時に、BREADY信号がアサートされた",$time);
                    else if (S_AXI_AWVALID & S_AXI_AWREADY) // アドレス転送終了
                        wr_tran_cs <= wr_data_tran;
                wr_data_tran :
                    if (S_AXI_BREADY) // エラー
                        $display("%m: at time %t Write データ転送中に、S_AXI_BREADY がアサートされた",$time);
                    else if (S_AXI_WVALID & S_AXI_WREADY & S_AXI_WLAST) // データ転送終了
                        wr_tran_cs <= wr_resp_tran;
                wr_resp_tran :
                    if (S_AXI_AWREADY | S_AXI_WVALID | S_AXI_WREADY | S_AXI_WLAST) // エラー
                        $display("%m: at time %t Write Response Channel 転送時に関連しない信号がアサートされた",$time);
                    else if (S_AXI_BVALID & S_AXI_BREADY) // Write Response Channel 転送終了
                        wr_tran_cs <= idle_wts;
            endcase
        end
    end
    
    // Read 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            rd_trans_cs <= idle_rts;
        else begin
            case (rd_trans_cs)
                idle_rts :
                    if (S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST)) // エラー
                        $display("%m: at time %t S_AXI_ARREADY がアサートされた時に、その他のVALID, READY信号がアサートされた",$time);
                    else if (S_AXI_ARVALID & S_AXI_ARREADY) // アドレス転送終了
                        rd_trans_cs <= rd_data_tran;
                rd_data_tran :
                    if (S_AXI_ARREADY) // エラー
                        $display("%m: at time %t Read データ転送中に、S_AXI_ARREADY がアサートされた",$time);
                    else if (S_AXI_RVALID & S_AXI_RREADY & S_AXI_RLAST) // データ転送終了
                        rd_trans_cs <= idle_rts;
            endcase
        end
    end
                        
endmodule    

`default_nettype wire



OVLは実行オプションが重要なので、コマンドラインでのコマンドを下に示す。

Command Line: fuse -intstyle ise -incremental -i K:/HDL/OVL/std_ovl_v2p6 -d OVL_VERILOG -d OVL_ASSERT_ON -d OVL_FINISH_OFF -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip -o H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_isim_beh.exe -prj H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_beh.prj work.CDC_axi_slave_tb work.glbl {-L accellera_ovl_vlog=K:\HDL\Xilinx\13.4\ISE_DS\ISE\verilog\hdp\nt\accellera_ovl_vlog}
Running: K:\HDL\Xilinx\14.1\ISE_DS\ISE\bin\nt\unwrapped\fuse.exe -intstyle ise -incremental -i K:/HDL/OVL/std_ovl_v2p6 -d OVL_VERILOG -d OVL_ASSERT_ON -d OVL_FINISH_OFF -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip -o H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_isim_beh.exe -prj H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_141/pcores/cdc_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb_beh.prj work.CDC_axi_slave_tb work.glbl -L accellera_ovl_vlog=K:\HDL\Xilinx\13.4\ISE_DS\ISE\verilog\hdp\nt\accellera_ovl_vlog

  1. 2012年10月25日 05:59 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.3(AXI Master BFMのVerilogコード)

前の記事は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”だが、全体の関連は、”AXI4 Slave IPの作製”を参照のこと。

今回はAXI Master BFMを貼っておく。AXI Master BFMと言っても、汎用的に使えるものではなく、トランザクションの並列実行に対応していない。キャラクタ・ディスプレイ・コントローラ AXI Slave IPの簡易的な単体テストとしては十分に使えると思う。

AXI Master BFMの Write の task がAXI_Master_1Seq_Write だ。このtaskは、(Write Address, Write Data), Write Response をシーケンシャルにオーバーラップせずに行う。今回の修正で、(Write Address, Write Data)はfork ~ join を使用して、オーバーラップするように変更した。AXI_Master_1Seq_Writeは、8個の引数を持つ。wait_clk_bready、wmax_wait 以外の信号はAXIバスの信号なので、説明は省く。wait_clk_bready はWriteのデータ転送が終了して、BREADYをアサートするまでのWaitクロック数を設定する。Writeデータ転送時にランダムな数のWaitが入るが、wmax_wait は、そのWait の最大値を指示する。

AXI Master BFMの Read の task が、AXI_Master_1Seq_Read で、Read Address, Read Data をシーケンシャルに行う。

下に、AXI Master BFM を示す。(2013/12/14:AWCACHE、ARCACHEを 3 に変更しました、input に DELAYを入れるように変更しました。)

// AXI4 bus Master Bus Fucntion Mode
// 2012/10/24 : 修正、S_AXI_AWREADYが1になるのを確認してからS_AXI_WVALIDを1にしていたのでは、AXIバスの非標準となる。
// よって、AXI_MASTER_WAC とAXI_MASTER_WDC をfork ~ join で並列に実行する
// 2013/12/14 : input に DELAYを入れるように変更
//

`default_nettype none

`timescale 100ps / 1ps

module AXI4_Master_BFM #(
    parameter DELAY    = 10 )
(
    input    wire    ACLK,

    output reg [0:0] S_AXI_AWID = 0,
    output reg [31:0] S_AXI_AWADDR = 0,
    output reg [7:0] S_AXI_AWLEN = 0,
    output reg [2:0] S_AXI_AWSIZE = 0,
    output reg [1:0] S_AXI_AWBURST = 0,
    output reg [1:0] S_AXI_AWLOCK = 0,
    output reg [3:0] S_AXI_AWCACHE = 3,    // Normal Non-cacheable bufferable
    output reg [2:0] S_AXI_AWPROT = 0,
    output reg [3:0] S_AXI_AWREGION = 0,
    output reg [3:0] S_AXI_AWQOS = 0,
    output reg [0:0] S_AXI_AWUSER = 0,
    output reg S_AXI_AWVALID = 0,
    output reg [0:0] S_AXI_WID = 0,
    output reg [31:0] S_AXI_WDATA = 0,
    output reg [3:0] S_AXI_WSTRB = 0,
    output reg S_AXI_WLAST = 0,
    output reg [0:0] S_AXI_WUSER = 0,
    output reg S_AXI_WVALID = 0,
    output reg S_AXI_BREADY = 0,
    output reg [0:0] S_AXI_ARID = 0,
    output reg [31:0] S_AXI_ARADDR = 0,
    output reg [7:0] S_AXI_ARLEN = 0,
    output reg [2:0] S_AXI_ARSIZE = 0,
    output reg [1:0] S_AXI_ARBURST = 0,
    output reg [1:0] S_AXI_ARLOCK = 0,
    output reg [3:0] S_AXI_ARCACHE = 2, // Normal Non-cacheable bufferable
    output reg [2:0] S_AXI_ARPROT = 0,
    output reg [3:0] S_AXI_ARREGION = 0,
    output reg [3:0] S_AXI_ARQOS = 0,
    output reg [0:0] S_AXI_ARUSER = 0,
    output reg S_AXI_ARVALID = 0,
    output reg S_AXI_RREADY = 0,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    reg     [7:0]    awlen_hold = 0;
    reg     [0:0]    wid_hold = 0;
    reg     axi_w_transaction_active = 0;
    reg     axi_r_transaction_active = 0;
    reg     [7:0]    arlen_hold = 0;

    reg     S_AXI_AWREADY_d;
    reg     S_AXI_WREADY_d;
    reg     [0:0] S_AXI_BID_d;
    reg     [1:0] S_AXI_BRESP_d;
    reg     [0:0] S_AXI_BUSER_d;
    reg     S_AXI_BVALID_d;
    reg     S_AXI_ARREADY_d;
    reg     [0:0] S_AXI_RID_d;
    reg     [31:0] S_AXI_RDATA_d;
    reg     [1:0] S_AXI_RRESP_d;
    reg     S_AXI_RLAST_d;
    reg     [0:0] S_AXI_RUSER_d;
    reg     S_AXI_RVALID_d;

    always @* S_AXI_AWREADY_d <= #DELAY S_AXI_AWREADY;
    always @* S_AXI_WREADY_d <= #DELAY S_AXI_WREADY;
    always @* S_AXI_BID_d <= #DELAY S_AXI_BID;
    always @* S_AXI_BRESP_d <= #DELAY S_AXI_BRESP;
    always @* S_AXI_BUSER_d <= #DELAY S_AXI_BUSER;
    always @* S_AXI_BVALID_d <= #DELAY S_AXI_BVALID;
    always @* S_AXI_ARREADY_d <= #DELAY S_AXI_ARREADY;
    always @* S_AXI_RID_d <= #DELAY S_AXI_RID;
    always @* S_AXI_RDATA_d <= #DELAY S_AXI_RDATA;
    always @* S_AXI_RRESP_d <= #DELAY S_AXI_RRESP;
    always @* S_AXI_RLAST_d <= #DELAY S_AXI_RLAST;
    always @* S_AXI_RUSER_d <= #DELAY S_AXI_RUSER;
    always @* S_AXI_RVALID_d <= #DELAY S_AXI_RVALID;

    // Write Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_Master_1Seq_Write;    // Write Address; Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        input    [31:0]    wdata;
        input    [7:0]    wait_clk_bready;
        input    [7:0]    wmax_wait;
        begin
            fork
                AXI_MASTER_WAC(awid, awaddr, awlen, awsize, awburst);
                AXI_MASTER_WDC(wdata, wmax_wait);
            join
            AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask

    // Write Address Channel
    task AXI_MASTER_WAC;
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        begin
            S_AXI_AWID        = awid;
            S_AXI_AWADDR    = awaddr;
            S_AXI_AWLEN        = awlen;
            S_AXI_AWSIZE    = awsize;
            S_AXI_AWBURST    = awburst;
            S_AXI_AWVALID    = 1'b1;

            if (axi_w_transaction_active == 1'b0) begin // AXI Write トランザクションが開始されている場合は戻る
                axi_w_transaction_active = 1'b1; // AXIトランザクション開始

                awlen_hold        = awlen; // Write Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_AWREADY_d) begin    // S_AXI_AWREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end

                #DELAY;
                S_AXI_AWID         = 0;
                S_AXI_AWADDR    = 0;
                S_AXI_AWLEN     = 0;
                S_AXI_AWSIZE     = 0;
                S_AXI_AWBURST     = 0;
                S_AXI_AWVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
        end
    endtask

    // Write Data Channel
    // wmax_wait : 0 - wvalid の Wait は無し、0以外 - wmax_wait を最大値とするランダムな値の Wait が wvalid に入る
    task AXI_MASTER_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input    [31:0]    wdata;
        input    [7:0]    wmax_wait;    // Write時の最大wait数
        integer    i, j, val;
        begin
            i = 0; j = 0;
            S_AXI_WSTRB = 4'b1111;

            while (~S_AXI_AWVALID) begin    // S_AXI_AWVALID が1になるまで待つ
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end

            while (i<=awlen_hold) begin
                if (wmax_wait == 0) // wmax_wait が0の時は$random を実行しない
                    val = 0;
                else
                    val = $unsigned($random) % (wmax_wait+1);

                if (val == 0) begin // waitなし
                    S_AXI_WVALID = 1'b1;
                end else begin // waitあり
                    S_AXI_WVALID = 1'b0;
                    for (j=0; j<val; j=j+1) begin
                        @(posedge ACLK);    // 次のクロックへ
                        #DELAY;
                    end
                    S_AXI_WVALID = 1'b1; // wait終了
                end

                if (i == awlen_hold)
                    S_AXI_WLAST = 1'b1;
                else
                    S_AXI_WLAST = 1'b0;
                S_AXI_WDATA = wdata;
                wdata = wdata + 1;

                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_WREADY_d) begin    // S_AXI_WREADY が0の時は1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;

                i = i + 1;
            end
            S_AXI_WVALID = 1'b0;
            S_AXI_WLAST = 1'b0;
            S_AXI_WSTRB = 4'b0000;
        end
    endtask

    // Write Response Channel
    // wait_clk_bready : 0 - bready の Wait は無し、0以外 - bready の Wait は wait_clk_bready の値の Wait が入る
    task AXI_MASTER_WRC;    // wait_clk_bready
        input    [7:0]    wait_clk_bready;
        integer    i;
        begin
            for (i=0; i<wait_clk_bready; i=i+1) begin
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end

            S_AXI_BREADY = 1'b1;


            @(posedge ACLK);    // 次のクロックへ

            while (~S_AXI_BVALID_d) begin // S_AXI_BVALID が1になるまでWait
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;

            S_AXI_BREADY = 1'b0;

            axi_w_transaction_active = 1'b0; // AXIトランザクション終了
            @(posedge ACLK);
            #DELAY;
        end
    endtask

    // Read Channel
    task AXI_Master_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        begin
            AXI_MASTER_RAC(arid, araddr, arlen, arsize, arburst);
            AXI_MASTER_RDC(rmax_wait);
        end
    endtask

    // Read Address Channel
    task AXI_MASTER_RAC;
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            S_AXI_ARID         = arid;
            S_AXI_ARADDR    = araddr;
            S_AXI_ARLEN        = arlen;
            S_AXI_ARSIZE    = arsize;
            S_AXI_ARBURST    = arburst;
            S_AXI_ARVALID     = 1'b1;

            if (axi_r_transaction_active == 1'b0) begin // AXI Read トランザクションが開始されている場合は戻る
                arlen_hold    =arlen; // Read Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ

                while (~S_AXI_ARREADY_d) begin    // S_AXI_ARREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end

                #DELAY;
                S_AXI_ARID         = 0;
                S_AXI_ARADDR    = 0;
                S_AXI_ARLEN     = 0;
                S_AXI_ARSIZE     = 0;
                S_AXI_ARBURST     = 0;
                S_AXI_ARVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_r_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask

    // Read Data Channel
    task AXI_MASTER_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        integer i, val;
        begin
            while (~(S_AXI_RLAST_d & S_AXI_RVALID_d & S_AXI_RREADY)) begin // S_AXI_RLAST & S_AXI_RVALID & S_AXI_RREADY で終了
                if (rmax_wait == 0) begin // rmax_wait が0の時は$random を実行しない
                    val = 0;
                    S_AXI_RREADY = 1'b1;
                end else begin
                    val = $unsigned($random) % (rmax_wait+1);
                    if (val == 0)
                        S_AXI_RREADY = 1'b1;
                    else
                        S_AXI_RREADY = 1'b0;
                end
                #DELAY;

                for (i=0; i<val; i=i+1) begin // ランダム値でWait、val=0の時はスキップ
                    @(posedge ACLK);    // 次のクロックへ
                    #DELAY;
                end

                S_AXI_RREADY = 1'b1;
                @(posedge ACLK);    // 次のクロックへ
                while (~S_AXI_RVALID_d) begin // S_AXI_RVALID が1になるまでWait
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
            end
            #DELAY;

            S_AXI_RREADY = 1'b0;
            axi_r_transaction_active = 1'b0; // AXIトランザクション終了
            @(posedge ACLK);
            #DELAY;
        end
    endtask

endmodule

`default_nettype wire


  1. 2012年10月25日 05:50 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタROMをAXI4 Lite Slave として実装する8(SDK2)

キャラクタROMをAXI4 Lite Slave として実装する7(SDK1)”の続き。

前回はXMDを使用してキャラクタROMのドットデータをReadしたが、今度はソフトウェアを作成して表示した。前回確認できたがaxi_uartlite がstdin, stdoutに割り振られていたので、TeraTerm を起動してXPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDRからのアドレスのオフセット値を入力して、そのアドレスのキャラクタROMのドットデータを表示した。
TeraTermの設定メニューの端末の設定は以前と同様とした。下の図に示す。
AXI_P_Monitor_19_120605.png

TeraTermを立ちあげてシリアルに接続して前回同様にアドレスオフセット0x600, 0x604をReadした。
AXI_Lite_Slave_44_120619.png

XMDでReadした時と同様に、0x18, 0x24をRead出来た。

下にソフトウェアを示す。

/* * char_rom_axi_lite_test.c * *  Created on: 2012/06/17 *      Author: Masaaki */

#include "xbasic_types.h"
#include "xio.h"
#include "mb_interface.h"
#include "xparameters.h"
#include "stdio.h"

int main()
{
    unsigned char c;
    unsigned char hex_char[20];
    unsigned int addr, cr_data;
    int j;

    for(;;){
        xil_printf("Please input Character ROM Address ");
        for(j=0; j<19; j++){
            c = getc(stdin);
            if (c == '\r'){
                hex_char[j] = c;
                break;
            }else
                hex_char[j] = c; // 文字列に代入
        }
        addr = 0;
        for(j=0; j<19; j+=2){ // 文字の解析
            if (hex_char[j] == '\r')
                break;
            else{
                addr = addr << 4// 4ビット左シフト
                if (hex_char[j]>=0x30 && hex_char[j]<=0x39// 数字
                    addr += hex_char[j] - 0x30;
                else if (hex_char[j]>=0x41 && hex_char[j]<=0x46// A ~ F
                    addr += hex_char[j] - 0x41 + 10;
                else if (hex_char[j]>=0x61 && hex_char[j]<=0x66// a ~ f
                    addr += hex_char[j] - 0x61 + 10;
            }
        }
        xil_printf("\n");
        cr_data = *(volatile unsigned int *)(XPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDR+addr);
        xil_printf("Character ROM Data = %x\n", cr_data);
    }
}


なぜ文字の解析で j を +2 するかというと、getc()で1文字読んだ後に必ず 0 が入っているからだ。6、0と入力すると、'6', 0, '0', 0 と文字列に入っている。
  1. 2012年06月19日 04:50 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:2

キャラクタROMをAXI4 Lite Slave として実装する7(SDK1)

キャラクタROMをAXI4 Lite Slave として実装する6(シミュレーション)”の続き。

前回、PlanAheadで論理合成、インプリメントが終了したが、microblaze_0 のメモリ領域が8Kバイトだったので、getc() を使用するため32Kバイトまで増やした。これで論理合成、インプリメントが終了した。
次に、ハードウェア情報をExportしてSDKを起動する。

・PlanAheadのFileメニューからExport -> Export Hardware... を選択した。

・ダイアログが開く。Launch SDKにチェックを入れて、OKボタンをクリックした。
PlanAhead141_82_120518.png

・SDKが起動する。以前のプロジェクトが入っている。”XPS入りのPlanAhead14.1プロジェクトを作る3(SDK)”参照。
AXI_Lite_Slave_32_120617.png

system_hw_platform -> system.xml はchar_rom_axi_lite_0 の項目が入っているが、以前作成したAtlys_bsp_0 のxparameters.h にはchar_rom_axi_lite_0 の情報はない。もう一度、NewメニューからXilinx Board Support Package を作る必要があるようだ。

・FileメニューからNew -> Xilinx Board Support Package を選択する。

・New Board Support Package Project ダイアログが出る。Project name にAtlys_CharR_bsp_0 と入力して、Finishボタンをクリックした。
AXI_Lite_Slave_33_120617.png

・Board Support Package Settings ダイアログが開いた。
AXI_Lite_Slave_34_120617.png

・左のペインからstandalone をクリックすると、stdin, stdout にaxi_uartlite_0 が割り当てられていることがわかる。OKボタンをクリックした。
AXI_Lite_Slave_35_120617.png

・Atlys_CharR_bsp_0 が生成された。こちらのxparameters.h を開くと、CHAR_ROM_AXI_LITE_0の定義が見つかった。
AXI_Lite_Slave_36_120617.png

・次にCプロジェクトを作成する。FileメニューからNew -> Xilinx C Project を選択する。

・New Xilinx C Project でSelect Project Templete でEmpty Application を選択し、Project Name にchar_rom_axi_lite_test と入力して、Next > ボタンをクリックした。
AXI_Lite_Slave_37_120617.png

・Target an existing Board Support Package のラジオボタンをクリックして、Atlys_CharR_bsp_0 を指定した。Finishボタンをクリックした。
AXI_Lite_Slave_38_120617.png

・char_rom_axi_lite_test のプロジェクトが作成された。
AXI_Lite_Slave_39_120617.png

・次にCファイルを新規作成する。FileメニューからNew -> Source File を選択した。

・New Source File ダイアログのSource file にchar_rom_axi_lite_test.c を入力して、Finishボタンをクリックした。
AXI_Lite_Slave_40_120617.png

・char_rom_axi_lite_test.c が生成された。Cコードを書いてみた。
AXI_Lite_Slave_41_120617.png

SDKのProgram FPGAダイアログからFPGAをコンフィグ使用としたが、やはりエラーだったので、PlanAheadでiMPACTを立ちあげてコンフィグした。(”AXI Performance Monitor IPを試してみた2(SDK)”参照)

・SDKのProject Explorerで、char_rom_axi_lite_testの下のBinariesの下のchar_rom_axi_lite_test.elfで右クリックして、右クリックメニューからDebug As -> Lanunch on Hardware を選択する。
AXI_Lite_Slave_42_120618.png

・ダイアログが2つ出てOKバタンをクリックしていくと、デバック画面になる。

・真ん中上のウインドウにXMDウインドウがあるので、そのタブをクリックしてXMDウインドウにした。

・mrd 0x7f800600, mrd 0x7f800604 コマンドを入れてキャラクタROMをReadしてみた。
AXI_Lite_Slave_43_120618.png

結果は0x00000018と0x00000024で、”キャラクタROMをAXI4 Lite Slave として実装する6(シミュレーション)”の最後のシミュレーション波形の結果と同様になった。
AXI_Lite_Slave_31_120616.png
  1. 2012年06月18日 05:34 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタROMをAXI4 Lite Slave として実装する6(シミュレーション)

キャラクタROMをAXI4 Lite Slave として実装する5(インプリメント)”の続き。
(2012/06/18:修正)char_rom_axi_lite.v を32ビット幅データバスのアドレスに変更しました。

前回でインプリメントは終了したが、まだchar_rom_axi_lite の単体テストを行ってなかった。今回はchar_rom_axi_lite の単体でのシミュレーションを行う。

・まずは、ISEのプロジェクトを作成して、char_rom_axi_lite.v とchar_gen_rom.v をプロジェクトに加えた。
AXI_Lite_Slave_30_120616.png

・New Source... を使って、char_rom_axi_lite.v のテストベンチchar_rom_axi_lite_tb.v のテンプレートを作成した。

・char_rom_axi_lite_tb.v にVerilog コードを書き足して、テストベンチを完成させた。char_rom_axi_lite_tb.v を下に示す。

`timescale 100ps / 1ps
`default_nettype wire

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   18:53:35 06/14/2012
// Design Name:   char_rom_axi_lite
// Module Name:   H:/HDL/FndtnISEWork/Spartan6/Atlys/test/Atlys_EDK_test_PA/Atlys_EDK_test_PA.srcs/sources_1/edk/system/pcores/char_rom_axi_lite_v1_00_a/char_rom_axi_lite/char_rom_axi_lite_tb.v
// Project Name:  char_rom_axi_lite
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: char_rom_axi_lite
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module char_rom_axi_lite_tb;

    // Inputs
    wire ACLK;
    wire ARESETN;
    reg [31:0] S_AXI_AWADDR = 0;
    reg [2:0] S_AXI_AWPROT = 0;
    reg S_AXI_AWVALID = 0;
    reg [31:0] S_AXI_WDATA = 0;
    reg [3:0] S_AXI_WSTRB = 0;
    reg S_AXI_WVALID = 0;
    reg S_AXI_BREADY = 0;
    reg [31:0] S_AXI_ARADDR = 0;
    reg [2:0] S_AXI_ARPROT = 0;
    reg S_AXI_ARVALID = 0;
    reg S_AXI_RREADY = 0;

    // Outputs
    wire S_AXI_AWREADY;
    wire S_AXI_WREADY;
    wire [1:0] S_AXI_BRESP;
    wire S_AXI_BVALID;
    wire S_AXI_ARREADY;
    wire [31:0] S_AXI_RDATA;
    wire [1:0] S_AXI_RRESP;
    wire S_AXI_RVALID;

    parameter    DELAY = 10;
    
    // Instantiate the Unit Under Test (UUT)
    char_rom_axi_lite uut (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY), 
        .S_AXI_BRESP(S_AXI_BRESP), 
        .S_AXI_BVALID(S_AXI_BVALID), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY), 
        .S_AXI_RDATA(S_AXI_RDATA), 
        .S_AXI_RRESP(S_AXI_RRESP), 
        .S_AXI_RVALID(S_AXI_RVALID), 
        .S_AXI_RREADY(S_AXI_RREADY)
    );

    // clk_gen のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );
     
    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );
     
    initial begin
        // Initialize Inputs
        S_AXI_AWADDR = 0;
        S_AXI_AWPROT = 0;
        S_AXI_AWVALID = 0;
        S_AXI_WDATA = 0;
        S_AXI_WSTRB = 0;
        S_AXI_WVALID = 0;
        S_AXI_BREADY = 0;
        S_AXI_ARADDR = 0;
        S_AXI_ARPROT = 0;
        S_AXI_ARVALID = 0;
        S_AXI_RREADY = 0;

        // Wait Reset rising edge
        @(posedge ARESETN);
        
        // Add stimulus here
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_RAC(32'h0000_0100);
        AXI_MASTER_RDC;
        #DELAY;
        @(posedge ACLK);    // 次のクロックへ        
        #DELAY;
        AXI_MASTER_RAC(32'h0000_0180);
        AXI_MASTER_RDC;
        #DELAY;
        AXI_MASTER_RAC(32'h0000_0181);
        @(posedge ACLK);    // 次のクロックへ        
        #DELAY;
        AXI_MASTER_RDC;

    end
    
    // Read Address Channel    
    task AXI_MASTER_RAC;
        input    [31:0]    araddr;
        begin
            S_AXI_ARADDR    = araddr;
            S_AXI_ARVALID     = 1'b1;
            @(posedge ACLK);    // 次のクロックへ

            while (~S_AXI_ARREADY) begin    // S_AXI_ARREADY が1になるまで待つ
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            
            #DELAY;
            S_AXI_ARADDR    = 0;
            S_AXI_ARVALID     = 1'b0;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
    endtask
    
    // Read Data Channel
    task AXI_MASTER_RDC; 
        begin
            S_AXI_RREADY = 1'b1;

            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            S_AXI_RREADY = 1'b0;
        end
    endtask

endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


そう言えば、char_rom_axi_lite.v も貼ってなかったと思うので、下に示す。

`timescale 1ns/1ps

module char_rom_axi_lite #
    (
    parameter integer C_S_AXI_ADDR_WIDTH            = 32,
    parameter integer C_S_AXI_DATA_WIDTH            = 32
    )
    (
    // System Signals
    input wire ACLK,
    input wire ARESETN,

    // Slave Interface Write Address Ports
    input  wire [C_S_AXI_ADDR_WIDTH-1:0]   S_AXI_AWADDR,
    input  wire [3-1:0]                  S_AXI_AWPROT,
    input  wire                          S_AXI_AWVALID,
    output wire                          S_AXI_AWREADY,

    // Slave Interface Write Data Ports
    input  wire [C_S_AXI_DATA_WIDTH-1:0]   S_AXI_WDATA,
    input  wire [C_S_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
    input  wire                          S_AXI_WVALID,
    output wire                          S_AXI_WREADY,

    // Slave Interface Write Response Ports
    output wire [2-1:0]                 S_AXI_BRESP,
    output wire                         S_AXI_BVALID,
    input  wire                         S_AXI_BREADY,

    // Slave Interface Read Address Ports
    input  wire [C_S_AXI_ADDR_WIDTH-1:0]   S_AXI_ARADDR,
    input  wire [3-1:0]                  S_AXI_ARPROT,
    input  wire                          S_AXI_ARVALID,
    output wire                          S_AXI_ARREADY,

    // Slave Interface Read Data Ports
    output wire [C_S_AXI_DATA_WIDTH-1:0]  S_AXI_RDATA,
    output wire [2-1:0]                 S_AXI_RRESP,
    output wire                         S_AXI_RVALID,
    input  wire                         S_AXI_RREADY

    );

    // RESP の値の定義
    parameter    RESP_OKAY =        2'b00;
    parameter    RESP_EXOKAY =    2'b01;
    parameter    RESP_SLVERR =     2'b10;
    parameter    RESP_DECERR =    2'b11;
    
    parameter    IDLE_RD    =        3'b001,
                AR_DATA_WAIT =    3'b010,
                AR_DATA_VALID =    3'b100;
    reg        [2:0]    rdt_cs;
    
    wire    [7:0]    char_data;
    reg        [31:0]    char_data_1d;
    reg        rvalid;
    
    // char_gen_rom のインスタンス
    char_gen_rom char_gen_rom_inst (
        .clk(ACLK),
        .reset(~ARESETN),
        .char_addr(S_AXI_ARADDR[11:5]),
        .row_addr(S_AXI_ARADDR[4:2]),
        .dout(char_data)
    );
    
    // AXI4 LITEのReadトランザクション用ステートマシン
    always @(posedge ACLK) begin
        if (~ARESETN) begin
            rdt_cs <= IDLE_RD;
            rvalid <= 1'b0;
        end else begin
            case (rdt_cs)
                IDLE_RD :
                    if (S_AXI_ARVALID)
                        rdt_cs <= AR_DATA_WAIT;
                AR_DATA_WAIT : begin
                    rdt_cs <= AR_DATA_VALID;
                    rvalid <= 1'b1;
                end
                AR_DATA_VALID :
                    if (S_AXI_RREADY) begin
                        rdt_cs <= IDLE_RD;
                        rvalid <= 1'b0;
                    end
            endcase
        end
    end
    assign S_AXI_RVALID = rvalid;
    
    always @(posedge ACLK) begin
        if (~ARESETN)
            char_data_1d <= 8'd0;
        else begin
            if (rdt_cs == AR_DATA_WAIT)
                char_data_1d <= {24'd0, char_data};
        end
    end
    assign S_AXI_RDATA = char_data_1d;

    assign S_AXI_ARREADY = 1'b1;
    assign S_AXI_RRESP = RESP_OKAY;
    
    // Writeポートの処理、Writeが間違ってきた時にデッドロックしないように、すべてイネーブルとした
    assign S_AXI_AWREADY = 1'b1;
    assign S_AXI_WREADY = 1'b1;
    assign S_AXI_BRESP = RESP_OKAY;
    assign S_AXI_BVALID = 1'b1;
endmodule


・ISimでシミュレーションを行った。後ろ2つのAXI4 Lite Readトランザクションを下に示す。
AXI_Lite_Slave_31_120616.png

シミュレーションはうまく行ったが、やはり最初にシミュレーションした時にはバグがあった。バグを修正した。
もう一度、PlanAheadで論理合成、インプリメントを行なって、今度はSDKでソフトウェアを作成する。
  1. 2012年06月16日 04:28 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタROMをAXI4 Lite Slave として実装する5(インプリメント)

キャラクタROMをAXI4 Lite Slave として実装する4(UARTをXPSプロジェクトに追加する)”の続き。

今回はPlanAheadでインプリメントを行う。

最初に、XPSプロジェクトのsystem にUARTのピンを追加したので、トップのVerilog ファイルを再生成する。
・XPSプロジェクトのsystem の右クリックメニューからCreate Top HDLを選択した。

・system_stub.v が再生成された。再生成されたsystem_stub.v を下に示す。

//-----------------------------------------------------------------------------
// system_stub.v
//-----------------------------------------------------------------------------

module system_stub
  (
    RESET,
    Led,
    CLK,
    axi_uartlite_0_RX_pin,
    axi_uartlite_0_TX_pin
  );
  input RESET;
  output [0:7] Led;
  input CLK;
  input axi_uartlite_0_RX_pin;
  output axi_uartlite_0_TX_pin;

  (* BOX_TYPE = "user_black_box" *)
  system
    system_i (
      .RESET ( RESET ),
      .Led ( Led ),
      .CLK ( CLK ),
      .axi_uartlite_0_RX_pin ( axi_uartlite_0_RX_pin ),
      .axi_uartlite_0_TX_pin ( axi_uartlite_0_TX_pin )
    );

endmodule


・次に、system_stub.ucf ファイルに、UARTのピンの制約を追加した。system_stub.ucf ファイルを下に示す。

NET "CLK" LOC = "L15" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L42P_GCLK7_M1UDM, Type = GCLK, Sch name = GCLK
NET "RESET" LOC = "T15" | IOSTANDARD = LVCMOS33; # Bank = 2, Pin name = IO_L1N_M0_CMPMISO_2, Sch name = M0/RESET
NET "Led<0>" LOC = "U18" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L52N_M1DQ15, Sch name = LD0
NET "Led<1>" LOC = "M14" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L53P, Sch name = LD1
NET "Led<2>" LOC = "N14" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L53N_VREF, Sch name = LD2
NET "Led<3>" LOC = "L14" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L61P, Sch name = LD3
NET "Led<4>" LOC = "M13" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L61N, Sch name = LD4
NET "Led<5>" LOC = "D4" | IOSTANDARD = LVCMOS33; # Bank = 0, Pin name = IO_L1P_HSWAPEN_0, Sch name = HSWAP/LD5
NET "Led<6>" LOC = "P16" | IOSTANDARD = LVCMOS33; # Bank = 1, Pin name = IO_L74N_DOUT_BUSY_1, Sch name = LD6
NET "Led<7>" LOC = "N12" | IOSTANDARD = LVCMOS33; # Bank = 2, Pin name = IO_L13P_M1_2, Sch name = M1/LD7
NET "axi_uartlite_0_RX_pin" IOSTANDARD = LVCMOS33;
NET "axi_uartlite_0_RX_pin" LOC = A16;
NET "axi_uartlite_0_TX_pin" IOSTANDARD = LVCMOS33;
NET "axi_uartlite_0_TX_pin" LOC = B16;


これでインプリメントの準備は完了したので、論理合成、インプリメントを行ったところ成功した。
AXI_Lite_Slave_29_120614.png
  1. 2012年06月14日 04:49 |
  2. AX4 Lite Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0
»