SPI Master送信モジュールの設計と実装

1 概要

SPI通信におけるマスターモジュールの設計では、MOSIとSCLKの2本の信号線を正しく制御することが重要です。本稿では、CPHAおよびCPOLの4通りの組み合わせ(CPHA=0/CPOL=0、CPHA=1/CPOL=0、CPHA=0/CPOL=1、CPHA=1/CPOL=1)に対応可能なSPI送信モジュールの設計と実装について説明します。

2 モジュール構成

2.1 モジュール構成図

設計するSPI送信モジュールは、2つのモジュールから構成されます。トップモジュールspi_master_txと送信ドライバモジュールui_mspi_txです。このようにモジュールを分割することで、コード構造が明確になり保守性も向上します。

2.2 モジュール構成要素

送信モジュールには以下のサブモジュールが含まれます:
  • クロック分周器
  • データ制御モジュール
  • 並列→直列変換モジュール
  • CPOL制御モジュール
  • CPHA制御モジュール

3 送信ドライバの実装

3.1 クロック分周器

システムクロックからSPI用クロック(SCLK)を生成するために分周器を設けます。SPI通信が開始されたときに分周カウンタが動作します。
`timescale 1ns / 1ps
module clock_divider(
    input      I_clk,
    input      I_rstn,
    input      I_enable,
    output reg O_sclk
);

parameter CLK_DIV = 10'd50;
reg [9:0] clk_counter = 10'd0;
reg sclk = 1'b0;

always @(posedge I_clk or negedge I_rstn) begin
    if (!I_rstn) begin
        clk_counter <= 10'd0;
        sclk <= 1'b0;
    end else if (I_enable) begin
        if (clk_counter < CLK_DIV) begin
            clk_counter <= clk_counter + 1'b1;
            if (clk_counter == CLK_DIV/2)
                sclk <= 1'b1;
            else if (clk_counter == CLK_DIV)
                sclk <= 1'b0;
        end else begin
            clk_counter <= 10'd0;
        end
    end else begin
        clk_counter <= 10'd0;
        sclk <= 1'b0;
    end
end

assign O_sclk = sclk;

endmodule

3.2 並列→直列変換モジュール

送信データは8ビットの並列データとして入力され、1ビットずつシリアル出力されます。CPHA設定に応じてデータ更新タイミングを調整します。
module parallel_to_serial(
    input      I_clk,
    input      I_rstn,
    input      I_enable,
    input      I_update,
    input [7:0] I_data_in,
    output reg [7:0] O_data_shift,
    output reg O_bit_out
);

always @(posedge I_clk or negedge I_rstn) begin
    if (!I_rstn) begin
        O_data_shift <= 8'h00;
    end else if (I_enable && I_update) begin
        O_data_shift <= {O_data_shift[6:0], 1'b0};
    end else if (I_enable) begin
        O_data_shift <= I_data_in;
    end
end

assign O_bit_out = O_data_shift[7];

endmodule

3.3 データ制御モジュール

送信要求信号I_spi_tx_reqに基づいてデータをバッファし、送信状態を管理します。送信中はO_spi_busy信号で通知します。
module data_controller(
    input      I_clk,
    input      I_rstn,
    input      I_req,
    input [7:0] I_data,
    input      I_update_complete,
    output reg O_enable,
    output reg [7:0] O_data_out,
    output reg O_busy
);

reg req_detected = 1'b0;

always @(posedge I_clk or negedge I_rstn) begin
    if (!I_rstn) begin
        O_enable <= 1'b0;
        O_data_out <= 8'h00;
        O_busy <= 1'b0;
    end else begin
        if (I_req && !req_detected) begin
            O_enable <= 1'b1;
            O_data_out <= I_data;
            O_busy <= 1'b1;
            req_detected <= 1'b1;
        end else if (I_update_complete) begin
            O_enable <= 1'b0;
            O_busy <= 1'b0;
            req_detected <= 1'b0;
        end
    end
end

endmodule

4 シミュレーションと検証

4.1 シミュレーション環境

ModelSimを用いてRTLレベルのシミュレーションを行います。システムクロックとリセット信号を生成し、モジュールの動作を確認します。
`timescale 1ns / 1ps
module testbench();
    reg sys_clk = 1'b0;
    reg rst_n = 1'b0;
    wire sclk;
    wire mosi;

    // モジュールインスタンス
    spi_master_tx uut (
        .I_clk(sys_clk),
        .I_rstn(rst_n),
        .O_spi_sclk(sclk),
        .O_spi_mosi(mosi)
    );

    initial begin
        #20 rst_n = 1'b1;
        #10000 $finish;
    end

    always #10 sys_clk = ~sys_clk;

endmodule

4.2 CPHAとCPOLの動作検証

以下にCPHAとCPOLの4通りの組み合わせでの動作を確認します:
  • CPHA=0, CPOL=0:SCLKはアイドル時にLow、最初の立ち上がりエッジでデータをサンプリング
  • CPHA=1, CPOL=0:SCLKはアイドル時にLow、2番目の立ち上がりエッジでデータをサンプリング
  • CPHA=0, CPOL=1:SCLKはアイドル時にHigh、最初の立ち下がりエッジでデータをサンプリング
  • CPHA=1, CPOL=1:SCLKはアイドル時にHigh、2番目の立ち下がりエッジでデータをサンプリング
各モードにおけるデータの立ち上がり/立ち下がりタイミングをシミュレーション波形で確認し、仕様通りに動作することを検証します。

タグ: SPI通信 FPGA設計 Verilog HDL Anlogic FPGA クロック制御

5月24日 02:22 投稿