最近在做一个需要高速SPI通信的项目,目标是在160MHz时钟频率下实现稳定传输。经过三天调试,终于解决了所有时序问题,实测波形质量堪比示波器厂商的广告图。本文将分享完整的Verilog实现方案,包括主机和从机代码,以及调试过程中积累的宝贵经验。
SPI(Serial Peripheral Interface)是一种同步串行通信接口,广泛应用于芯片间通信。在FPGA设计中,实现高速SPI接口需要考虑时钟域处理、建立保持时间、信号完整性等多个关键因素。本次实现的方案采用CPOL=0/CPHA=0模式(时钟空闲时为低电平,数据在上升沿采样),通过独特的时钟分频技术实现了稳定的160MHz通信。
传统SPI主机设计通常使用PLL生成专用SPI时钟,但本方案采用了更为巧妙的四分频技术:
verilog复制module spi_master(
input clk, // 640MHz主时钟
input start,
output reg sclk,
output reg mosi,
input miso,
output reg cs,
output reg [7:0] data_out
);
reg [1:0] clk_div;
reg [3:0] bit_cnt;
reg [7:0] tx_data;
reg [7:0] rx_buffer;
always @(posedge clk) begin
if(start) begin
clk_div <= clk_div + 1;
// 四分频生成160MHz SPI时钟
sclk <= (clk_div == 2'b10) ? 1'b1 :
(clk_div == 2'b00) ? 1'b0 : sclk;
end
end
这种设计有三大优势:
注意:使用此方案的前提是FPGA主时钟频率必须是SPI时钟频率的整数倍(这里是4倍关系)
SPI数据传输的核心是一个精确定时的状态机:
verilog复制// 状态机处理数据移位
always @(posedge clk) begin
if(start) begin
case(clk_div)
2'b01: begin // 时钟上升沿前准备数据
mosi <= tx_data[7];
tx_data <= {tx_data[6:0], 1'b0};
end
2'b11: begin // 时钟下降沿采样
rx_buffer <= {rx_buffer[6:0], miso};
bit_cnt <= bit_cnt + 1;
end
endcase
end
end
状态机工作原理:
在160MHz高速通信下,时序优化至关重要。以下是几个关键技巧:
采样点选择:最初尝试在SPI时钟上升沿直接采样MISO,结果时序违规。改为在clk_div==2'b11(相当于SPI时钟下降沿后半个主时钟周期)采样,建立保持时间完美满足。
寄存器布局:将所有SPI相关寄存器放在同一SLICE中,减少布线延迟。
时钟分配:使用专用时钟布线资源分配SPI时钟,降低时钟偏斜。
高速SPI从机的核心挑战是避免亚稳态。本设计采用双缓冲结构:
verilog复制module spi_slave(
input sclk,
input mosi,
output miso,
input cs,
output reg [7:0] recv_data
);
reg [7:0] shift_reg;
reg [7:0] next_data;
always @(posedge sclk) begin
if(!cs) begin
shift_reg <= {shift_reg[6:0], mosi}; // 正沿采样
end
end
always @(negedge sclk) begin
if(!cs) begin
miso <= next_data[7]; // 负沿更新输出
next_data <= {next_data[6:0], 1'b0};
end
end
// 双缓冲防止亚稳态
always @(posedge sclk) begin
if(cs) begin
recv_data <= shift_reg;
end
end
endmodule
设计亮点:
在160MHz频率下,亚稳态风险显著增加。本设计采取以下防护措施:
输入同步:虽然代码中没有显式展示,但在实际工程中应在SPI输入信号进入FPGA后立即添加两级同步寄存器。
时钟域隔离:接收数据(recv_data)只在CS拉高时更新,确保数据稳定。
时序约束:对跨时钟域信号添加set_false_path约束,避免工具进行不必要的优化。
使用Siglent SDS1104X-E示波器捕获的波形显示:
这些指标完全满足160MHz SPI通信的要求,甚至优于许多专用SPI接口芯片的性能。
令人惊讶的是,在Vivado中未添加任何时序约束的情况下,实现结果居然没有时序违例。分析原因:
经验分享:虽然本设计在没有时序约束的情况下也能工作,但生产环境建议还是添加适当的约束,特别是针对I/O延迟的约束。
问题现象:在早期版本中,MISO采样时序不满足要求。
解决方案:
问题现象:SPI时钟抖动较大,导致接收错误。
解决方案:
问题现象:从机响应第一个bit时存在延迟。
解决方案:
对于需要更高性能的应用,可以考虑以下优化方向:
使用DDR技术:在时钟上升沿和下降沿都传输数据,等效将速率提升一倍。
添加流水线:对数据处理部分添加流水线寄存器,提高系统时钟频率。
使用FPGA专用资源:如Xilinx的ISERDESE2/OSERDESE2,实现更可靠的串并转换。
信号完整性优化:
在实际项目中,我发现在160MHz频率下,信号完整性的影响开始变得显著。建议在PCB设计阶段就做好仿真,确保信号质量。另外,FPGA的IO标准选择也很关键,LVCMOS33在高速情况下表现不如LVDS或HSTL。