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番目の立ち下がりエッジでデータをサンプリング
各モードにおけるデータの立ち上がり/立ち下がりタイミングをシミュレーション波形で確認し、仕様通りに動作することを検証します。