我用 Verilog 写了一个 SPI 从机。那里有一些实现,但我仍在学习 Verilog 和一般的数字逻辑,所以我决定尝试自己编写它。
我的实施有效。但是我必须进行更改才能使其正常工作,并且进行更改(我认为)使我的实现与摩托罗拉 SPI 规范不一致。我想知道:我认为这很奇怪是对的,还是我只是不明白某些东西是如何工作的?
SPI 信号通过称为 SCK、SS、MOSI 和 MISO 的四根线输入。那里并不奇怪。FPGA 以 12MHz 运行,SPI 总线以 1MHz 运行。该策略是在每个 FPGA 时钟频率上采样总线 SPI 总线,并跟踪 SCK 和 SS 的当前值和最后值,以便我可以检测边缘。我还将每个信号通过缓冲区寄存器。所以逻辑总是在实际事件之后运行两个 FPGA 时钟:在时钟 1 上,我将信号锁存到缓冲区中;在时钟 2 上,我将其复制到用于逻辑的 reg 中,在时钟 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 总线后面运行了两个 FPGA 周期,但我仍然有 12 个 FPGA 时钟用于每一个 SCK 滴答,这意味着我在 SCK negedge 和下一个 posedge 之间有六个 FPGA 周期。我应该能够及时了解 MISO 的内容,对吧?
在实践中,每个人是否都只是(在 SPI 模式 0 中)在 SCK posege 之后立即更新 MISO 而不必担心 negedge?
谢谢!