FPGAでシンプルSPI受信

FPGA

ESP32マイコンをSPIマスター,FPGAをSPIスレーブとして受信のみを行うVerilogソースを紹介します.

設計指針

  •  初心者向けになるべく簡素にしました
  •  SPIのMOSI受信動作のみで,MISO応答は返しません.
  •  グローバルクロックに同期した設計をしています.
  •  メタステーブル対策の入力FFあり

入力仕様

項目仕様備考
FPGAグローバルクロック10.64MHzLattic製 MachXO2の内臓PLLモジュール(OSCH)を使用
SPIクロック周波数1MHz1us周期
ByteオーダーMSBファースト 
データ長32bitESP32のSPI.transfer32関数を使用
SPIモードクロック待機時 High
データサンプリング 立上り
ESP32の設定はSPI.setDataMode(SPI_MODE3)
チップセレクトLow有意 

出力仕様

  • 32bit受信データをパラレル出力します.
  • SPIチップセレクトの立上りを検出し,パラレル出力の有効を示すValid信号を1サイクルアサートします.

RTL図

SPI受信回路RTL図

Verilogソースコード

//=============================================================
// SPI recever for ESP32
//-------------------------------------------------------------
// SPI clock polarity : idole:high , sampling rise edge	:SPI.setDataMode(SPI_MODE3) 
// SPI clock freqency : 1MHz        :SPI.setFrequency(1000000)
// SPI byte order     : MSB first   :SPI.setBitOrder(MSBFIRST)
// SPI data length    : 32bit       :SPI.transfer32(32bit_data)
//=============================================================
module SPI_RECEIVER_32BIT
  (input	wire      i_clk       //10.64MHz global clock
  ,input	wire      i_rst_n     //Low active global async reset
  ,input	wire      i_SPI_CLK   //1MHz = 1us
  ,input	wire      i_SPI_CS_n  //Chip select Low active
  ,input	wire      i_SPI_MOSI  //
  ,output	reg[31:0] o_data      //receive data
  ,output	reg       o_valid     //receive data valid 
  );
    
  reg [2:0]	buf_clk;
  reg [2:0]	buf_ena;
  reg [2:0]	buf_dat;
  
  reg [31:0]rsv_data;
  wire      clk_rise;
  wire      ena_rise;
  wire      shift	;
    
//input buffer FF to eliminate metastable. :メタステーブル対策入力FF
  always@(posedge i_clk or negedge i_rst_n)begin
    if(!i_rst_n)begin
        buf_clk	<=3'b111;// idle state is High
        buf_ena	<=3'b111;// idle state is High
        buf_dat	<=3'b000;
    end else begin
        buf_clk	<={buf_clk[1:0] , i_SPI_CLK};
        buf_ena	<={buf_ena[1:0] , i_SPI_CS_n};
        buf_dat	<={buf_dat[1:0] , i_SPI_MOSI};
    end
  end
    
//edge detect:SPI入力信号のエッジ検出
  assign clk_rise=( (buf_clk[2]==1'b0) && (buf_clk[1]==1'b1) ) ? 1'b1 : 1'b0;
  assign ena_rise=( (buf_ena[2]==1'b0) && (buf_ena[1]==1'b1) ) ? 1'b1 : 1'b0;
  assign shift   =( (clk_rise == 1'b1) && (buf_ena[2]==1'b0) ) ? 1'b1 : 1'b0;

//data shift convert serial to parallel:SPIデータシリパラ変換用FF
  always@(posedge i_clk)begin
    if(shift)
      rsv_data <= {rsv_data[30:0],buf_dat[2]};
  end

//output regester:出力段FF
  always@(posedge i_clk)begin
    if(!i_rst_n)begin
      o_data	<=32'd0;
      o_valid <=1'b0;
    end else if(ena_rise==1'b1)begin
      o_data	<=rsv_data;
      o_valid <=1'b1;
    end else begin
      o_data	<=o_data;
      o_valid <=1'b0;
    end
  end

endmodule

設計ポイント

グローバルクロック同期設計

FPGA設計のセオリーに従って,全てのフリップフロップはグローバルクロックに同期するように設計します.
つまり,SPI_CLKはクロックとして使用せずに入力信号として設計します.
この場合,グローバルクロックがSPIクロックより十分に早いことが必須になります.
1MHzのSPIクロックに対して約10倍早い10.64MMzのグローバルクロックを使用しているので,本回路構成は成立します.

メタステーブル対策

メタステーブルについての説明は割愛しますが,グローバルクロックに同期した後の信号のみを使用します.
入力ポートから入ってきた信号はただの2段D-FFを通過させた後に,組み合わせ回路に接続する構成としています.

シミュレーション波形

コメント

タイトルとURLをコピーしました