読者です 読者をやめる 読者になる 読者になる

Lynx-EyEDの電音鍵盤 新館

広帯域制御屋の駄文とか

8bit SPIスレーブの実装

  • FPGA4FUNのSPIスレーブVerilogコードがあまり実用的でなかったので書き直したメモです。

fpga4funのSPIコードでは、CS(SSEL)信号やSCK、MOSIの状態を3bitのシフトレジスタにコピーし、立ち上がり・立ち下がりを観測している様です。

// sync SCK to the FPGA clock using a 3-bits shift register
reg [2:0] SCKr;  always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
wire SCK_risingedge = (SCKr[2:1]==2'b01);  // now we can detect SCK rising edges
wire SCK_fallingedge = (SCKr[2:1]==2'b10);  // and falling edges

// same thing for SSEL
reg [2:0] SSELr;  always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
wire SSEL_active = ~SSELr[1];  // SSEL is active low
wire SSEL_startmessage = (SSELr[2:1]==2'b10);  // message starts at falling edge
wire SSEL_endmessage = (SSELr[2:1]==2'b01);  // message stops at rising edge

// and for MOSI
reg [1:0] MOSIr;  always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
wire MOSI_data = MOSIr[1];

なんとも半端ない努力ですが *1 、SPIの転送速度がFPGAクロックに支配されてしまいます。
そこで、マスターから送出されるSCKに同期するようにしたものです。要はただの8bitシフトレジスタです。

  • SCLKをFPGAクロックに依存しないようにした
  • SPIでマスターから受け取った8bitデータをエコーバックするVerilogコード。
  • 使用ロジックエレメントは20LEほど。(ALTERA MAXII CPLD)

軽い説明:
マスタからの8bitデータをMOSI端子からSCKクロックに同期して受け取ります。
1バイト分受け取ると byte_received = 1 になります。マスタから受け取った8bitデータはシフトレジスタ:byte_data_receivedに格納されています
byte_received が1になったら、送信用レジスタ:byte_data_sent にbyte_data_receivedをそのままコピーします。これによって次の8bitサイクルでマスタにデータがエコーバックされます。
全二重通信なのでマスタはスレーブデータを受け取るために0xffなどのダミーデータを送る必要はありません。スレーブからのデータを受け取りつつ、次のデータを送信できます。
このデータは次々回の1バイト送受信のときにスレーブから返ってくるはずです。

module SPI_slave(SCK, MOSI, MISO, SSEL, MOSI_GET_DATA);

input		SCK, SSEL, MOSI;
output		MISO;
output [7:0]	MOSI_GET_DATA;



wire byte_received;  // high when a byte has been received
reg [7:0] byte_data_received;
reg [2:0] bitcnt;

always @(posedge SCK)begin
	if(SSEL) begin		//if(CS = 1)
		bitcnt <= 3'b000;
	end else begin
		bitcnt <= bitcnt + 3'b001;
	end;
end

    // implement a shift-left register (since we receive the data MSB first)
always @(posedge SCK) byte_data_received <= {byte_data_received[6:0], MOSI};

assign byte_received = (~SSEL) && (bitcnt==3'b111); 
assign MOSI_GET_DATA = byte_data_received;


reg [7:0] byte_data_sent;
always @(negedge SCK)
if(~SSEL)begin
	if(byte_received) begin
		byte_data_sent <= byte_data_received;
   end else begin
      byte_data_sent <= {byte_data_sent[6:0], 1'b0};
	end;	
end	

	
assign MISO = byte_data_sent[7];

endmodule

*1:これはSPIの極性(CPHA、CPOL)の変更に柔軟に対応するためみたい。でもこのコードだと、動作可能SPIクロックは速くてもメインクロックの3分の1になってしまう