Verilog で SPI スレーブを作成しました。そこにはいくつかの実装がありますが、私はまだ Verilog とデジタル ロジック全般を学んでいるので、自分で書いてみることにしました。
私の実装は機能します。しかし、それを機能させるには変更を加える必要がありました。変更を加えると (私は思うに)、実装が Motorola SPI 仕様と矛盾します。私は疑問に思っています:これは奇妙だと思うのは正しいですか、それとも何かがどのように機能するかを理解していないだけですか?
SPI 信号は、SCK、SS、MOSI、および MISO と呼ばれる 4 つのワイヤで受信されます。そこに驚きはありません。FPGA は 12MHz で動作し、SPI バスは 1MHz で動作しています。戦略は、すべての FPGA クロック ポーズエッジでバス SPI バスをサンプリングし、SCK と SS の現在と最後の値の両方を追跡して、エッジを検出できるようにすることです。また、バッファ reg を介して各信号を渡します。そのため、ロジックは常に実際のイベントより 2 FPGA クロック遅れて動作しています。クロック 1 で、信号をバッファにラッチします。クロック 2 でロジックに使用するレジスタにコピーし、クロック 3 でそれを実行します。
SPI モード 0 を使用しています。SPI 仕様によると、モード 0 では、スレーブはSCK のポーズエッジで MOSI ラインをサンプリングし、SCK のネゲエッジで送信する必要があります。
だから私はちょうどそのように書いた:
reg [511:0] data; // I exchange 512-bit messages with the master.
reg [2:0] SCK_buffer = 0;
always @(posedge clock) begin // clock is my FPGA clock
SCK_buffer <= {SCK_buffer[1:0], SCK};
end
wire SCK_posedge = SCK_buffer[2:1] == 2'b01;
wire SCK_negedge = SCK_buffer[2:1] == 2'b10;
reg [2:0] SS_buffer = 0;
always @(posedge clock) begin
SS_buffer <= {SS_buffer[1:0], SS};
end
wire SS_posedge = SS_buffer[2:1] == 2'b01;
wire SS_negedge = SS_buffer[2:1] == 2'b10;
wire SS_active = ~SS_buffer[1];
reg [1:0] MOSI_buffer = 0;
always @(posedge clock) begin
MOSI_buffer = {MOSI_buffer[0], MOSI};
end
wire MOSI_in = MOSI_buffer[1];
assign MISO = data[511];
always @(posedge clock) begin
if (SS_active) begin
if (SCK_posedge) begin
// Clock goes high: capture one bit from MOSI.
MOSI_capture <= MOSI_in;
end
else if (SCK_negedge) begin
// Shift the captured MOSI bit into the LSB of data and output
// the MSB from data. Note: MISO is a wire that outputs data[511].
data <= {data[510:0], MOSI_capture};
end
end
end
コードは次のように動作するはずです。
- SS がアクティブ (ロー) になると、データ [511] はワイヤによって MISO に既に出力されています。(バスに乗っているのは私だけなので、ここではトライステートはありません。)
- SCK のポーズエッジで、スレーブは MOSI をサンプリングし、マスターは MISO でデータ [511] を取得します。
- SCK のネゲッジで、スレーブは MOSI からサンプリングされたビットをデータにシフトし、新しいビットを MISO 出力位置にシフトさせます。
このバージョンを実行すると、あちこちにビットが落ちました。文字通り、マスターからスレーブに送信した 512 ビット メッセージの半分が壊れてしまいました。
オープンソースの SPI スレーブ実装を調べたところ、SCK の negedge が使用されていないことに気付きました。そこで、コードを次のように変更しました。
always @(posedge clock) begin
if (SS_active) begin
if (SCK_posedge) begin
// Skip that "capture" business and just shift MOSI right into the
// data reg.
data <= {data[510:0], MOSI_in};
end
end
end
この変更を行った後、完全に機能しました。完全防弾。
しかし、私は考えていますね?
これで大丈夫だということがわかりました。MISO は、SCK のポーズエッジの直後にその新しい値を取得し、マスターがそれをサンプリングするとき、次のポーズエッジでもそこにあります。しかし、negedge で MISO を変更したことの何が問題だったのでしょうか。バッファリングのために SPI バスの後ろで 2 つの FPGA サイクルを実行していますが、SCK の 1 ティックごとに 12 の FPGA クロックがまだあります。間に合うように MISO で少し公開できるはずですよね?
実際には、誰もが (SPI モード 0 で) SCK のポーズエッジの直後に MISO を更新し、ネゲエッジを気にしませんか?
ありがとう!