Grokで回路設計➂

昨日はI2CのACK/NACKを追加するところまで行いました。

そろそろ、検証してみたいと思います。

Grokにつぎの質問をしてみました。

この回路をテストするためのAPB側のテストパターンを作成してください

ここから

APB側からI2Cマスター回路をテストするためのテストベンチを以下に示します。このテストベンチは基本的な書き込み/読み出し操作とI2C転送のテストを行います。

systemverilog

`timescale 1ns/1ps

module tb_apb_i2c_master;

    // テストベンチ信号
    logic        pclk;
    logic        presetn;
    logic [31:0] paddr;
    logic        psel;
    logic        penable;
    logic        pwrite;
    logic [31:0] pwdata;
    logic [31:0] prdata;
    logic        pready;
    wire         scl;
    wire         sda;

    // DUTインスタンス
    apb_i2c_master dut (
        .pclk(pclk),
        .presetn(presetn),
        .paddr(paddr),
        .psel(psel),
        .penable(penable),
        .pwrite(pwrite),
        .pwdata(pwdata),
        .prdata(prdata),
        .pready(pready),
        .scl(scl),
        .sda(sda)
    );

    // クロック生成
    initial begin
        pclk = 0;
        forever #5 pclk = ~pclk;  // 100MHz clock
    end

    // I2Cスレーブモデル(簡易版)
    logic sda_drive;
    logic sda_out;
    assign sda = sda_drive ? sda_out : 1'bz;

    // テストシーケンス
    initial begin
        // 初期化
        presetn  = 0;
        psel     = 0;
        penable  = 0;
        pwrite   = 0;
        paddr    = 0;
        pwdata   = 0;
        sda_drive = 0;
        sda_out  = 1;

        #20;
        presetn = 1;
        #20;

        // テスト1: 設定レジスタへの書き込み(クロック分周設定)
        apb_write(32'h0C, 32'd100);  // 100サイクルでSCLトグル

        // テスト2: データレジスタへの書き込み
        apb_write(32'h08, 32'hA5);   // 送信データ 0xA5

        // テスト3: ステータスレジスタの読み出し
        apb_read(32'h04);

        // テスト4: I2C転送開始
        apb_write(32'h00, 32'h1);    // 制御レジスタ: Start

        // スレーブ側でACKを返す
        @(negedge scl) wait(dut.bit_counter == 8);
        @(negedge scl);
        sda_drive = 1;
        sda_out   = 0;  // ACK
        @(posedge scl);
        sda_drive = 0;

        // 転送完了待ち
        wait(dut.status_reg[1] == 0);  // busyフラグが下がるまで待つ
        #100;

        // テスト5: NACKの場合のテスト
        apb_write(32'h08, 32'h5A);   // 新しいデータ
        apb_write(32'h00, 32'h1);    // Start

        // スレーブ側でNACKを返す
        @(negedge scl) wait(dut.bit_counter == 8);
        @(negedge scl);
        sda_drive = 1;
        sda_out   = 1;  // NACK
        @(posedge scl);
        sda_drive = 0;

        wait(dut.status_reg[1] == 0);
        #100;

        // 結果確認
        apb_read(32'h04);  // ステータス確認

        #1000;
        $finish;
    end

    // APB書き込みタスク
    task apb_write(input [31:0] addr, input [31:0] data);
        @(posedge pclk);
        psel    = 1;
        penable = 0;
        pwrite  = 1;
        paddr   = addr;
        pwdata  = data;

        @(posedge pclk);
        penable = 1;

        @(posedge pclk);
        wait(pready);
        psel    = 0;
        penable = 0;
        pwrite  = 0;
        $display("Write: Addr=0x%h, Data=0x%h", addr, data);
    endtask

    // APB読み出しタスク
    task apb_read(input [31:0] addr);
        @(posedge pclk);
        psel    = 1;
        penable = 0;
        pwrite  = 0;
        paddr   = addr;

        @(posedge pclk);
        penable = 1;

        @(posedge pclk);
        wait(pready);
        $display("Read: Addr=0x%h, Data=0x%h", addr, prdata);
        psel    = 0;
        penable = 0;
    endtask

    // モニタリング
    initial begin
        $monitor("Time=%0t scl=%b sda=%b state=%s busy=%b nack=%b",
                 $time, scl, sda, dut.state.name(),
                 dut.status_reg[1], dut.status_reg[2]);
    end

    // 波形ダンプ
    initial begin
        $dumpfile("tb_apb_i2c_master.vcd");
        $dumpvars(0, tb_apb_i2c_master);
    end

endmodule

このテストベンチの主な特徴:

  1. テスト項目:
  • レジスタ書き込み/読み出しの確認
  • I2C転送の開始とデータ送信
  • ACK応答の処理
  • NACK応答の処理とエラー検出
  1. テストシーケンス:
  • CONFIGレジスタにクロック分周値を設定
  • DATAレジスタにテストデータを書き込み
  • CTRLレジスタで転送開始
  • スレーブ側でACK/NACKをシミュレート
  • STATUSレジスタで結果を確認
  1. サポート機能:
  • APBプロトコル用の書き込み/読み出しタスク
  • 簡易的なI2Cスレーブモデルの実装
  • 状態モニタリングと波形ダンプ

実行方法:

  1. このテストベンチと前述のapb_i2c_masterモジュールを同一ディレクトリに保存
  2. シミュレータ(例:ModelSim, VCSなど)でコンパイル&実行
  3. ログと波形を確認して動作を検証

期待される動作:

  • ACKテスト:0xA5が正常に送信され、転送が完了
  • NACKテスト:0x5Aの送信後NACKが検出され、ステータスに反映
  • ステータスのbusy/nackビットの変化を確認可能

このテストパターンをベースに、必要に応じてさらに詳細なテストケース(複数バイト転送、エラー条件など)を追加できます。

ここまで

基本的なテストパターンを生成してきました。

これを基にシナリオを増やせばテストも充実しそうですね。