FC2カウンター FPGAの部屋 AXI4-Lite インターフェースの I2C Master Core をシミュレーションする
fc2ブログ

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

FPGAの部屋

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

AXI4-Lite インターフェースの I2C Master Core をシミュレーションする

OpenCores.org の I2C controller core をシミュレーションする”で、OpenCores.org の WishBone バスの I2C controller core をシミュレーションしたが、これを Xilinx の Zynq で使用するために AXI4-Lite インターフェースで使用したいと思いった。
そこで、”AXI4 Master Bus Functional Model を修正した”と”AXI4-Lite Master Bus Functional Model を修正した”で AXI4-Lite の Master の BFM を用意した。
WishBone バスと AXI4-Lite インターフェースを変換する wb2axi4ls_conv.v とトップの i2cm_axi4ls.v”で、WishBone バスと AXI4-Lite インターフェースを変換する wb2axi4ls_conv.v とトップの i2cm_axi4ls.v を用意した。
AXI4-Lite インターフェースの I2C Master Core をシミュレーションしてみよう。

Vivado 2020.2 の i2cm_axi4ls プロジェクトを示す。
Vitis_Vision_disp_32_210422.png

階層図を示す。
Vitis_Vision_disp_33_210422.png

論理シミュレーションを行った。
Vitis_Vision_disp_34_210422.png

全体波形を示す。
Vitis_Vision_disp_35_210422.png

最初に 0 番地に 0x29 を 4 番地に 0x00 を Write して Read するトランザクションを示す。
Vitis_Vision_disp_36_210422.png

うまく行っているようだ。

次に、I2C のトランザクションがスタートしてから 0x010 番地を Read してビット 2 を関しているところを示す。
現在の Read の値は 0x02 になっているが、これは TIP ビットが立っていて I2C が busy ということを示す。
Vitis_Vision_disp_37_210422.png

I2C のトランザクションが終了して、 0x010 番地を Read した時に 0x41 になっているのが、黄色いカーソルの部分だ。
次のAXI4-Lite インターフェースの Write トランザクションが見える。
Vitis_Vision_disp_38_210422.png

うまく行っているようだ。

テストベンチファイルの i2cm_axi4ls_tb.v を示す。
なお、コード中のコメント部分は”OpenCores.org の I2C controller core ”のマニュアルから引用した。

// i2cm_axi4ls_tb.v
// 2021/04/13 by marsee
// コード中のコメント部分は”OpenCores.org の I2C controller core ”のマニュアルから引用した
// https://opencores.org/projects/i2c

`default_nettype none

`timescale 100ps / 1ps

module i2cm_axi4ls_tb;
    parameter DELAY    = 10;

    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 12; // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32; // Data width of the AXI Lite Interface

    // Quoted from tst_bench_top.v of I2C controller core on OpenCores.org.
    // https://opencores.org/projects/i2c
    parameter PRER_LO = 12'h000;
    parameter PRER_HI = 12'h004;
    parameter CTR     = 12'h008;
    parameter RXR     = 12'h00C;
    parameter TXR     = 12'h00C;
    parameter CR      = 12'h010;
    parameter SR      = 12'h010;

    parameter TXR_R   = 12'h014; // undocumented / reserved output
    parameter CR_R    = 12'h018; // undocumented / reserved output

    parameter RD      = 1'b1;
    parameter WR      = 1'b0;
    parameter SADR    = 7'b0010_000;

    // Inputs
    reg ACLK;
    reg ARESETN;
    wire [C_S_AXI_LITE_ADDR_WIDTH-1:0] S_AXI_AWADDR;
    wire [2:0] S_AXI_AWPROT;
    wire S_AXI_AWVALID;
    wire [C_S_AXI_LITE_DATA_WIDTH-1:0] S_AXI_WDATA;
    wire [C_S_AXI_LITE_DATA_WIDTH/8-1:0] S_AXI_WSTRB;
    wire S_AXI_WVALID;
    wire S_AXI_BREADY;
    wire [C_S_AXI_LITE_ADDR_WIDTH-1:0] S_AXI_ARADDR;
    wire [2:0] S_AXI_ARPROT;
    wire S_AXI_ARVALID;
    wire S_AXI_RREADY;

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

    wire scl_pad_i;
    wire scl_pad_o;
    wire scl_padoen_o;
    wire sda_pad_i;
    wire sda_pad_o;
    wire sda_padoen_o;

    // InOut
    wire scl;
    wire sda;

    reg [31:0] rdata;

    always #50 ACLK = ~ACLK;

    // Instantiate the Unit Under Test (UUT)
    i2cm_axi4ls #(
        .C_S_AXI_LITE_ADDR_WIDTH(12),
        .C_S_AXI_LITE_DATA_WIDTH(32)
    ) i2cm_axi4ls_i (
        .s_axi_lite_aclk(ACLK),
        .s_axi_lite_resetn(ARESETN),
        .s_axi_lite_awaddr(S_AXI_AWADDR),
        .s_axi_lite_awvalid(S_AXI_AWVALID),
        .s_axi_lite_awready(S_AXI_AWREADY),
        .s_axi_lite_wdata(S_AXI_WDATA),
        .s_axi_lite_wstrb(S_AXI_WSTRB),
        .s_axi_lite_wvalid(S_AXI_WVALID),
        .s_axi_lite_wready(S_AXI_WREADY),
        .s_axi_lite_bresp(S_AXI_BRESP),
        .s_axi_lite_bvalid(S_AXI_BVALID),
        .s_axi_lite_bready(S_AXI_BREADY),
        .s_axi_lite_araddr(S_AXI_ARADDR),
        .s_axi_lite_arvalid(S_AXI_ARVALID),
        .s_axi_lite_arready(S_AXI_ARREADY),
        .s_axi_lite_rdata(S_AXI_RDATA),
        .s_axi_lite_rresp(S_AXI_RRESP),
        .s_axi_lite_rvalid(S_AXI_RVALID),
        .s_axi_lite_rready(S_AXI_RREADY),
        .scl_i(scl_pad_i),
        .scl_o(scl_pad_o),
        .scl_t(scl_padoen_o),
        .sda_i(sda_pad_i),
        .sda_o(sda_pad_o),
        .sda_t(sda_padoen_o)
    );

    // i2c slave model
    i2c_slave_model #(SADR) i2c_slave (
        .scl(scl),
        .sda(sda)
    );

    pullup p1(scl); // pullup scl line
    pullup p2(sda); // pullup sda line

    // AXI4_Lite_Master_BFM
    AXI4_Lite_Master_BFM #(
        .DELAY(DELAY),
        .C_S_AXI_LITE_ADDR_WIDTH(12),
        .C_S_AXI_LITE_DATA_WIDTH(32)
    ) LMBFMi(
        .ACLK(ACLK),
        .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)
    );

    scl_sda_buf cd_buf_i(
        .scl_pad_i(scl_pad_i),
        .scl_pad_o(scl_pad_o),
        .scl_padoen_o(scl_padoen_o),
        .sda_pad_i(sda_pad_i),
        .sda_pad_o(sda_pad_o),
        .sda_padoen_o(sda_padoen_o),
        .scl(scl),
        .sda(sda)
    );

    // test
    initial begin
        // Initialize Inputs
        ACLK = 0;
        ARESETN = 0;

        // Wait 100 ns for global reset to finish
        #1000;
        ARESETN = 1;
        #1000;

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

        LMBFMi.AXI_LiteM_1Seq_Write(PRER_LO, 32'h29, 0, 2); // address is PRER_LO, data Write = 0x22
        LMBFMi.AXI_LiteM_1Seq_Write(PRER_HI, 32'h0, 0, 2); // address is PRER_HI, data Write = 0x0

        #1000;    // Wait 100 ns
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(PRER_LO, 2, rdata);    // PRER_LO, rmax_wait=0
        LMBFMi.AXI_LiteM_1Seq_Read(PRER_HI, 2, rdata);    // PRER_HI, rmax_wait=0

        #1000;    // Wait 100 ns
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(CTR, 8'h80, 0, 2);    // enable core
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, {SADR,WR}, 0, 2 ); // present slave address, set write-bit
        LMBFMi.AXI_LiteM_1Seq_Write( CR, 8'h90, 0, 2 ); // set command (start, write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // send register address
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, 8'h01, 0, 2); // present slave's memory address
        LMBFMi.AXI_LiteM_1Seq_Write(CR, 8'h10, 0, 2); // set command (write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // send data byte
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, 8'ha5, 0, 2); // present slave's memory address
        LMBFMi.AXI_LiteM_1Seq_Write(CR, 8'h10, 0, 2); // set command (write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // send data byte for next register address (auto_inc)
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, 8'h5a, 0, 2); // present data
        LMBFMi.AXI_LiteM_1Seq_Write(CR, 8'h50, 0, 2); // set command (stop, write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // Read Transaction
        #1000;
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, {SADR,WR}, 0, 2 ); // present slave address, set write-bit
        LMBFMi.AXI_LiteM_1Seq_Write( CR, 8'h90, 0, 2 ); // set command (start, write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // send register address
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, 8'h01, 0, 2); // present slave's memory address
        LMBFMi.AXI_LiteM_1Seq_Write(CR, 8'h10, 0, 2); // set command (write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        // repeat start
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(TXR, {SADR,WR}, 0, 2 ); // present slave address, set write-bit
        LMBFMi.AXI_LiteM_1Seq_Write( CR, 8'h90, 0, 2 ); // set command (start, write)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Write(CR, 8'h68, 0, 2 ); // set command (read, nack, stop)

        // check tip bit
        #DELAY;
        LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata);
        while(S_AXI_RDATA[1])
            LMBFMi.AXI_LiteM_1Seq_Read(SR, 2, rdata); // SR bit1 TIP

        LMBFMi.AXI_LiteM_1Seq_Read(RXR, 2, rdata);
        $display("\nRead data = %x at time %t", rdata, $time);

        $finish;
    end
endmodule

`default_nettype wire

  1. 2021年04月24日 04:30 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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