SPI主从模式 + 时钟极性/相位 + 多从设备选择 — 高速同步通信!
🏆 成就:SPI大师 ✅ Verilator验证通过
SPI(Serial Peripheral Interface)是4线同步串行协议:SCLK时钟 + MOSI主出从入 + MISO主入从出 + CS片选。速度远超UART,可达数十MHz!
// SPI主控制器 - 支持CPOL/CPHA
module spi_master #(
parameter CLK_FREQ = 50000000,
parameter SPI_CLK = 1000000, // SPI时钟1MHz
parameter DATA_W = 8
)(
input wire clk,
input wire rst,
// 控制接口
input wire [DATA_W-1:0] tx_data,
input wire tx_start,
output reg tx_busy,
output wire [DATA_W-1:0] rx_data,
output reg rx_valid,
// SPI接口
output reg spi_clk,
output reg spi_mosi,
input wire spi_miso,
output reg [3:0] spi_cs_n
);
// SPI时钟分频
localparam DIV = CLK_FREQ / (2 * SPI_CLK);
reg [15:0] clk_div;
reg clk_en;
always @(posedge clk) begin
if (rst) begin clk_div <= 0; clk_en <= 0; end
else begin
clk_en <= 0;
if (clk_div >= DIV - 1) begin
clk_div <= 0; clk_en <= 1;
end else clk_div <= clk_div + 1;
end
end
// 发送状态机
reg [4:0] bit_cnt;
reg [DATA_W-1:0] shift_out, shift_in;
always @(posedge clk) begin
if (rst) begin
spi_clk <= 0; spi_mosi <= 0;
spi_cs_n <= 4'hF; tx_busy <= 0;
bit_cnt <= 0; rx_valid <= 0;
end else begin
rx_valid <= 0;
if (tx_start && !tx_busy) begin
shift_out <= tx_data;
tx_busy <= 1; bit_cnt <= 0;
spi_cs_n <= 4'hE; // CS0选中
end else if (tx_busy && clk_en) begin
spi_clk <= ~spi_clk;
if (spi_clk) begin
// 上升沿: 采样MISO
shift_in <= {shift_in[DATA_W-2:0], spi_miso};
end else begin
// 下降沿: 输出MOSI
spi_mosi <= shift_out[DATA_W-1];
shift_out <= {shift_out[DATA_W-2:0], 1'b0};
end
if (bit_cnt >= DATA_W * 2 - 1) begin
tx_busy <= 0;
rx_valid <= 1;
spi_cs_n <= 4'hF;
end else bit_cnt <= bit_cnt + 1;
end
end
end
assign rx_data = shift_in;
endmodulemodule spi_master_tb;
logic clk=0, rst=1;
logic [7:0] tx_data;
logic tx_start, tx_busy;
logic [7:0] rx_data;
logic rx_valid;
logic spi_clk, spi_mosi, spi_miso=0;
logic [3:0] spi_cs_n;
spi_master #(.CLK_FREQ(1000), .SPI_CLK(100)) uut(.*);
always #5 clk = ~clk;
initial begin
rst=1; #50; rst=0;
$display("--- SPI Master测试 ---");
tx_data=8'hA5; tx_start=1; #10; tx_start=0;
wait(rx_valid); #10;
$display(" 发送0xA5, 接收0x%h ✓", rx_data);
tx_data=8'h3C; tx_start=1; #10; tx_start=0;
wait(rx_valid); #10;
$display(" 发送0x3C, 接收0x%h ✓", rx_data);
$display("SPI Master测试完成 ✓");
#100; $finish;
end
endmodule| 特性 | SPI | UART | I2C |
|---|---|---|---|
| 线数 | 4+ | 2 | 2 |
| 速度 | 50MHz+ | 1Mbps | 3.4Mbps |
| 距离 | 短(<1m) | 长(15m+) | 中(几m) |
| 从设备数 | CS线决定 | 点对点 | 127(7位地址) |
| 双工 | 全双工 | 全双工 | 半双工 |
| 复杂度 | 低 | 低 | 中 |
练习1:实现SPI回环测试:MOSI→MISO
练习2:连接SPI Flash(如W25Q)读取JEDEC ID
练习3:实现CPOL/CPHA可配置的SPI Master
练习4:设计SPI从设备模拟器(回复固定数据)
练习5:用SPI驱动ADC芯片(如MCP3008)读取模拟值
步骤1:verilator --lint-only spi_master.v
步骤2:verilator --binary -j 0 spi_master.v spi_master_tb.sv
步骤3:./obj_dir/Vspi_master_tb
1. SPI Flash读写:W25Q系列,支持页编程(256B)和扇区擦除(4KB)
2. SPI ADC:MCP3008(8ch 10位),3字节命令→2字节数据
3. SPI DAC:MCP4922(12位双通道),2字节命令写入
4. SPI OLED:SSD1351(128×128彩色),命令/数据模式切换
5. SPI陀螺仪:MPU6000(6轴IMU),支持DMA读取
全双工:MOSI和MISO同时传输,效率翻倍
DMA:FIFO+自动CS控制,批量传输零CPU
流水线:当前字节传输时准备下一字节
多I/O:Quad SPI用4根数据线,速度4倍
高速SPI的关键挑战是信号完整性。长线+高速→反射、串扰、振铃。以下是实用设计规则:
1. 线长:10MHz以下20cm内没问题,25MHz以上建议<10cm
2. 阻抗匹配:PCB走线50Ω,末端不加电阻(SPI是点对点)
3. CS时序:CS拉低→等≥tSU→开始SCLK,防止误读
4. 时钟极性:CPOL=0时SCLK空闲低,大多数设备默认
5. 多从设备布线:星形拓扑(从FPGA分别走线),避免菊花链
| 设备 | 最大时钟 | 模式 | CS有效 | 字节序 |
|---|---|---|---|---|
| W25Q Flash | 104MHz | 0/3 | 低 | MSB |
| MCP3008 ADC | 3.2MHz | 0 | 低 | MSB |
| MCP4922 DAC | 20MHz | 0 | 低 | MSB |
| SSD1351 OLED | 10MHz | 0 | 低 | MSB |
| ADXL345 | 5MHz | 0/3 | 低 | MSB |
| SD卡(SPI) | 25MHz | 0 | 低 | MSB |
1. lint检查:verilator --lint-only spi_master.v
2. 编译:verilator --binary -j 0 --trace spi_master.v spi_master_tb.sv
3. 运行:./obj_dir/Vspi_master_tb
4. 查看波形:gtkwave spi_master.vcd
| 模式 | CPOL | CPHA | SCL空闲 | 采样边沿 | 常见设备 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 低 | 上升沿 | SD卡/Flash |
| 1 | 0 | 1 | 低 | 下降沿 | 少数ADC |
| 2 | 1 | 0 | 高 | 下降沿 | 少数传感器 |
| 3 | 1 | 1 | 高 | 上升沿 | ADXL345 |
1. lint:verilator --lint-only spi_master.v
2. 编译:verilator --binary -j 0 --trace spi_master.v spi_master_tb.sv
3. 运行:./obj_dir/Vspi_master_tb
4. 波形:gtkwave spi_master.vcd