Grokで回路設計➀

生成AIで回路設計が行えるようになってきました。

今回はGrokでI2Cのマスター回路の設計をしてみました。

質問内容はつぎのとおりです。

apbバスから制御できるi2cのmasterの回路をsystemverilogで作成してください

Grokの回答がつぎのとおりでした。

ここから

以下に、APBバスから制御可能なI2Cマスター回路のSystemVerilogコード例を示します。基本的な機能を実装したもので、APBインターフェース経由でI2Cの制御を行います。

systemverilog

module apb_i2c_master (
    // APB Interface
    input  logic        pclk,        // APB clock
    input  logic        presetn,     // APB reset (active low)
    input  logic [31:0] paddr,       // APB address
    input  logic        psel,        // APB select
    input  logic        penable,     // APB enable
    input  logic        pwrite,      // APB write enable
    input  logic [31:0] pwdata,      // APB write data
    output logic [31:0] prdata,      // APB read data
    output logic        pready,      // APB ready

    // I2C Interface
    inout  wire         scl,         // I2C clock
    inout  wire         sda          // I2C data
);

    // レジスタ定義
    localparam ADDR_CTRL    = 8'h00;  // 制御レジスタ
    localparam ADDR_STATUS  = 8'h04;  // ステータスレジスタ
    localparam ADDR_DATA    = 8'h08;  // データレジスタ
    localparam ADDR_CONFIG  = 8'h0C;  // 設定レジスタ

    // 内部レジスタ
    logic [31:0] ctrl_reg;    // 制御レジスタ
    logic [31:0] status_reg;  // ステータスレジスタ
    logic [31:0] data_reg;    // データレジスタ
    logic [31:0] config_reg;  // 設定レジスタ

    // I2C制御信号
    logic scl_out;
    logic sda_out;
    logic sda_oe;    // Output enable for SDA
    logic i2c_busy;
    logic i2c_start;
    logic i2c_stop;

    // APBバス制御
    logic [7:0] addr_byte;

    assign addr_byte = paddr[7:0];
    assign pready = 1'b1;  // 常にreadyを返すシンプルな実装

    // APB書き込み処理
    always_ff @(posedge pclk or negedge presetn) begin
        if (!presetn) begin
            ctrl_reg   <= 32'h0;
            data_reg   <= 32'h0;
            config_reg <= 32'h0;
        end else if (psel && penable && pwrite) begin
            case (addr_byte)
                ADDR_CTRL:   ctrl_reg   <= pwdata;
                ADDR_DATA:   data_reg   <= pwdata;
                ADDR_CONFIG: config_reg <= pwdata;
            endcase
        end
    end

    // APB読み出し処理
    always_comb begin
        case (addr_byte)
            ADDR_CTRL:   prdata = ctrl_reg;
            ADDR_STATUS: prdata = status_reg;
            ADDR_DATA:   prdata = data_reg;
            ADDR_CONFIG: prdata = config_reg;
            default:     prdata = 32'h0;
        endcase
    end

    // I2C制御ロジック
    typedef enum logic [2:0] {
        IDLE,
        START,
        DATA,
        STOP,
        WAIT
    } i2c_state_t;

    i2c_state_t state, next_state;
    logic [7:0] bit_counter;
    logic [7:0] tx_data;
    logic [31:0] clock_div;
    logic [31:0] clock_count;

    // クロック分周
    always_ff @(posedge pclk or negedge presetn) begin
        if (!presetn) begin
            clock_count <= 32'h0;
            scl_out <= 1'b1;
        end else begin
            if (clock_count >= config_reg) begin
                clock_count <= 32'h0;
                scl_out <= ~scl_out;
            end else begin
                clock_count <= clock_count + 1;
            end
        end
    end

    // I2Cステートマシン
    always_ff @(posedge pclk or negedge presetn) begin
        if (!presetn) begin
            state <= IDLE;
            sda_oe <= 1'b0;
            sda_out <= 1'b1;
            bit_counter <= 8'h0;
        end else begin
            state <= next_state;

            case (state)
                IDLE: begin
                    if (ctrl_reg[0]) begin // Start bit
                        sda_oe <= 1'b1;
                        sda_out <= 1'b1;
                        tx_data <= data_reg[7:0];
                    end
                end
                START: begin
                    sda_out <= 1'b0;
                end
                DATA: begin
                    if (scl_out && bit_counter < 8) begin
                        sda_out <= tx_data[7-bit_counter];
                        bit_counter <= bit_counter + 1;
                    end
                end
                STOP: begin
                    sda_out <= 1'b0;
                    sda_oe <= 1'b1;
                end
            endcase
        end
    end

    // 次状態ロジック
    always_comb begin
        next_state = state;
        i2c_busy = 1'b1;

        case (state)
            IDLE: begin
                i2c_busy = 1'b0;
                if (ctrl_reg[0]) next_state = START;
            end
            START: if (!scl_out) next_state = DATA;
            DATA: if (bit_counter == 8) next_state = STOP;
            STOP: if (scl_out) next_state = IDLE;
        endcase
    end

    // I2Cピンの割り当て
    assign scl = scl_out ? 1'bz : 1'b0;
    assign sda = sda_oe ? (sda_out ? 1'bz : 1'b0) : 1'bz;

    // ステータス更新
    assign status_reg = {30'h0, i2c_busy, ctrl_reg[0]};

endmodule

このコードの主な特徴:

  1. APBインターフェース:
  • 標準的なAPB信号(pclk, presetn, paddrなど)を実装
  • 4つのレジスタ(制御、ステータス、データ、設定)を定義
  • 読み書き可能なレジスタマッピング
  1. I2Cマスター機能:
  • 基本的なI2Cプロトコル(スタート、データ送信、ストップ)を実装
  • 設定可能なクロック分周器
  • 双方向ピン(scl, sda)の制御
  • ステートマシンによるシーケンス制御
  1. 主要なレジスタ:
  • CTRL: 動作開始/停止制御
  • STATUS: 現在の動作状態
  • DATA: 送受信データ
  • CONFIG: クロック分周設定

使用方法:

  1. APB経由でCONFIGレジスタにクロック分周値を設定
  2. DATAレジスタに送信したいデータを書き込み
  3. CTRLレジスタのビット0を1に設定して転送開始

この実装は基本的なもので、以下の機能を追加拡張できます: ACK/NACKの処理

  • リード操作のサポート
  • エラー処理
  • 複数バイト転送
  • 中断信号の生成

必要に応じてこれらの機能を追加実装することができます。

ここまで

検証は行っていませんが基本的な実装例ですが間違えていなさそうです。

生成したコードの解説も付いているのでたんにコードが生成されるだけでなく、コードの理解もしやすいでしょう。

今後は生成AIを使って回路設計をする方法も選択肢のひとつに成り得ると思います。